diff --git a/.github/ISSUE_TEMPLATE/bug-in-sample.md b/.github/ISSUE_TEMPLATE/bug-in-sample.md new file mode 100644 index 0000000000..ebe78110d9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-in-sample.md @@ -0,0 +1,43 @@ +--- +name: Bug in sample +about: Report a problem with a sample +title: '' +labels: bug +assignees: '' + +--- + +**Which sample are you reporting a bug in?** + +Example: ResizeAppView + +**Describe the bug** +A clear and concise description of what the bug is. +Include links to specific lines of code where you think the bug is. +(Click the line number, then click the "..." and select "Copy permalink.") +If not sure, at least include a link to the scenario where the problem is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Configuration** + +* Language/architecture/flavor: (example: C++/WinRT x64 Debug) +* Windows platform and build number: (example: Desktop build 18362.) + +* Visual Studio version: (example: 16.6.5; get this value from Help.About.) + + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..58ed9bf109 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Bug in the OS + url: https://developer.microsoft.com/windows/support/ + about: In Windows Dev Center, click "Forums & community" to see your options + - name: OS feature request + url: https://aka.ms/feedback-hub/support + about: Use Feedback Hub to request OS features diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md new file mode 100644 index 0000000000..81e8ef85ef --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -0,0 +1,19 @@ +--- +name: Suggestion for sample +about: Suggest a new sample or update to an existing sample +title: '' +labels: suggestion +assignees: '' + +--- + +**Which sample are you requesting a feature to be added to?** + +Example: ResizeAppView (or say "New sample" to suggest a new sample) + +Note that this is for suggesting a new sample or suggesting an update to an existing sample. To request features to be added to the operating system, use [Feedback Hub]( https://aka.ms/feedback-hub/support). + +**Describe the feature** +A clear and concise description of the desired feature to be demonstrated by a sample. + +Example: Show how to use the MapElementsLayer class to organize elements into layers on the MapControl. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000..4d983495be --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,41 @@ + + + +## Description + +Name of sample: (example: ResizeAppView, or proposed name of new sample) + + + + +## Testing + + + +## Type of change + +- [ ] Bug fix +- [ ] New feature +- [ ] Porting to new language + +## Supported platforms +Minimum OS version: (example: 18362) + + +- [ ] All UWP platforms +- [ ] Desktop +- [ ] Holographic +- [ ] IoT +- [ ] Xbox +- [ ] 10X + +## Supported languages + + + + +- [ ] C# +- [ ] C++/WinRT + +## Additional remarks + diff --git a/README.md b/README.md index 838d92caab..822ede9c42 100644 --- a/README.md +++ b/README.md @@ -531,11 +531,12 @@ For additional Windows samples, see [Windows on GitHub](http://microsoft.github. + - + diff --git a/Samples/BarcodeScanner/README.md b/Samples/BarcodeScanner/README.md index be9455ee3b..40c6079e99 100644 --- a/Samples/BarcodeScanner/README.md +++ b/Samples/BarcodeScanner/README.md @@ -5,6 +5,7 @@ languages: - cpp - cppcx - vb +- cppwinrt products: - windows - windows-uwp diff --git a/Samples/BarcodeScanner/cppwinrt/BarcodeScanner.sln b/Samples/BarcodeScanner/cppwinrt/BarcodeScanner.sln new file mode 100644 index 0000000000..07c7b42ab4 --- /dev/null +++ b/Samples/BarcodeScanner/cppwinrt/BarcodeScanner.sln @@ -0,0 +1,43 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30320.27 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BarcodeScanner", "BarcodeScanner.vcxproj", "{A8DD75F9-CA19-4997-96CC-F6C28FD7D55B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM = Release|ARM + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A8DD75F9-CA19-4997-96CC-F6C28FD7D55B}.Debug|ARM.ActiveCfg = Debug|ARM + {A8DD75F9-CA19-4997-96CC-F6C28FD7D55B}.Debug|ARM.Build.0 = Debug|ARM + {A8DD75F9-CA19-4997-96CC-F6C28FD7D55B}.Debug|ARM.Deploy.0 = Debug|ARM + {A8DD75F9-CA19-4997-96CC-F6C28FD7D55B}.Debug|x64.ActiveCfg = Debug|x64 + {A8DD75F9-CA19-4997-96CC-F6C28FD7D55B}.Debug|x64.Build.0 = Debug|x64 + {A8DD75F9-CA19-4997-96CC-F6C28FD7D55B}.Debug|x64.Deploy.0 = Debug|x64 + {A8DD75F9-CA19-4997-96CC-F6C28FD7D55B}.Debug|x86.ActiveCfg = Debug|Win32 + {A8DD75F9-CA19-4997-96CC-F6C28FD7D55B}.Debug|x86.Build.0 = Debug|Win32 + {A8DD75F9-CA19-4997-96CC-F6C28FD7D55B}.Debug|x86.Deploy.0 = Debug|Win32 + {A8DD75F9-CA19-4997-96CC-F6C28FD7D55B}.Release|ARM.ActiveCfg = Release|ARM + {A8DD75F9-CA19-4997-96CC-F6C28FD7D55B}.Release|ARM.Build.0 = Release|ARM + {A8DD75F9-CA19-4997-96CC-F6C28FD7D55B}.Release|ARM.Deploy.0 = Release|ARM + {A8DD75F9-CA19-4997-96CC-F6C28FD7D55B}.Release|x64.ActiveCfg = Release|x64 + {A8DD75F9-CA19-4997-96CC-F6C28FD7D55B}.Release|x64.Build.0 = Release|x64 + {A8DD75F9-CA19-4997-96CC-F6C28FD7D55B}.Release|x64.Deploy.0 = Release|x64 + {A8DD75F9-CA19-4997-96CC-F6C28FD7D55B}.Release|x86.ActiveCfg = Release|Win32 + {A8DD75F9-CA19-4997-96CC-F6C28FD7D55B}.Release|x86.Build.0 = Release|Win32 + {A8DD75F9-CA19-4997-96CC-F6C28FD7D55B}.Release|x86.Deploy.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F8F7BA01-CCD5-44F2-879D-3189E0598F3C} + EndGlobalSection +EndGlobal diff --git a/Samples/BarcodeScanner/cppwinrt/BarcodeScanner.vcxproj b/Samples/BarcodeScanner/cppwinrt/BarcodeScanner.vcxproj new file mode 100644 index 0000000000..54adbc6d5b --- /dev/null +++ b/Samples/BarcodeScanner/cppwinrt/BarcodeScanner.vcxproj @@ -0,0 +1,235 @@ + + + + + $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), LICENSE))\SharedContent + + + true + {A8DD75F9-CA19-4997-96CC-F6C28FD7D55B} + BarcodeScanner + SDKTemplate + en-US + 15.0 + true + Windows Store + 10.0 + 10.0.18362.0 + 10.0.17763.0 + + + + + Debug + ARM + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM + + + Release + Win32 + + + Release + x64 + + + + Application + Unicode + + + true + true + + + false + true + false + + + + + + + + $(VC_IncludePath);$(UniversalCRT_IncludePath);$(WindowsSDK_IncludePath);$(SharedContentDir)\cppwinrt + true + + + + Use + pch.h + $(IntDir)pch.pch + Level4 + %(AdditionalOptions) /bigobj /await + 4453;28204 + + + + + _DEBUG;%(PreprocessorDefinitions) + + + + + NDEBUG;%(PreprocessorDefinitions) + + + + + $(SharedContentDir)\xaml\App.xaml + + + $(SharedContentDir)\xaml\MainPage.xaml + + + + + + + ..\shared\Scenario1_BasicFunctionality.xaml + Code + + + + ..\shared\Scenario2_MultipleScanners.xaml + Code + + + Scenario3_ActiveSymbologies.xaml + Code + + + ..\shared\Scenario4_SymbologyAttributes.xaml + Code + + + ..\shared\Scenario5_DisplayingBarcodePreview.xaml + Code + + + + + + Designer + + + Designer + + + + + Designer + + + + + Styles\Styles.xaml + + + + + $(SharedContentDir)\xaml\App.xaml + + + $(SharedContentDir)\xaml\MainPage.xaml + + + BarcodeScannerInfo.h + + + BindingHelpers.h + + + DataHelpers.h + + + SampleConfiguration.h + + + ..\shared\Scenario1_BasicFunctionality.xaml + + + Create + pch.h + + + Project.idl + + + ..\shared\Scenario2_MultipleScanners.xaml + Code + + + Scenario3_ActiveSymbologies.xaml + Code + + + ..\shared\Scenario4_SymbologyAttributes.xaml + + + ..\shared\Scenario5_DisplayingBarcodePreview.xaml + Code + + + SymbologyListEntry.h + + + + + $(SharedContentDir)\xaml\MainPage.xaml + + + + + + Designer + + + + + + Assets\microsoft-sdk.png + + + Assets\smallTile-sdk.png + + + Assets\splash-sdk.png + + + Assets\squareTile-sdk.png + + + Assets\storeLogo-sdk.png + + + Assets\tile-sdk.png + + + Assets\windows-sdk.png + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/Samples/BarcodeScanner/cppwinrt/BarcodeScanner.vcxproj.filters b/Samples/BarcodeScanner/cppwinrt/BarcodeScanner.vcxproj.filters new file mode 100644 index 0000000000..da382ed588 --- /dev/null +++ b/Samples/BarcodeScanner/cppwinrt/BarcodeScanner.vcxproj.filters @@ -0,0 +1,74 @@ + + + + + 4416d50a-7676-4d0a-9b2c-91ff70c6047f + bmp;fbx;gif;jpg;jpeg;tga;tiff;tif;png + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + + + + + + + \ No newline at end of file diff --git a/Samples/BarcodeScanner/cppwinrt/BarcodeScannerInfo.cpp b/Samples/BarcodeScanner/cppwinrt/BarcodeScannerInfo.cpp new file mode 100644 index 0000000000..f5ef8d6c9f --- /dev/null +++ b/Samples/BarcodeScanner/cppwinrt/BarcodeScannerInfo.cpp @@ -0,0 +1,15 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "pch.h" +#include "BarcodeScannerInfo.h" +#include "BarcodeScannerInfo.g.cpp" + diff --git a/Samples/BarcodeScanner/cppwinrt/BarcodeScannerInfo.h b/Samples/BarcodeScanner/cppwinrt/BarcodeScannerInfo.h new file mode 100644 index 0000000000..13ce82bf44 --- /dev/null +++ b/Samples/BarcodeScanner/cppwinrt/BarcodeScannerInfo.h @@ -0,0 +1,47 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +#include "BarcodeScannerInfo.g.h" + +namespace winrt::SDKTemplate::implementation +{ + struct BarcodeScannerInfo : BarcodeScannerInfoT + { + BarcodeScannerInfo(winrt::hstring const& deviceName, winrt::hstring const& deviceId) : + m_deviceName(deviceName), + m_deviceId(deviceId) + { + } + + winrt::hstring Name() const + { + return m_deviceName; + } + + winrt::hstring DeviceId() const + { + return m_deviceId; + } + + private: + winrt::hstring m_deviceName; + winrt::hstring m_deviceId; + }; +} + +namespace winrt::SDKTemplate::factory_implementation +{ + struct BarcodeScannerInfo : BarcodeScannerInfoT + { + }; +} \ No newline at end of file diff --git a/Samples/BarcodeScanner/cppwinrt/BindingHelpers.cpp b/Samples/BarcodeScanner/cppwinrt/BindingHelpers.cpp new file mode 100644 index 0000000000..c2e8813691 --- /dev/null +++ b/Samples/BarcodeScanner/cppwinrt/BindingHelpers.cpp @@ -0,0 +1,14 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "pch.h" +#include "BindingHelpers.h" +#include "BindingHelpers.g.cpp" diff --git a/Samples/BarcodeScanner/cppwinrt/BindingHelpers.h b/Samples/BarcodeScanner/cppwinrt/BindingHelpers.h new file mode 100644 index 0000000000..02926d7a33 --- /dev/null +++ b/Samples/BarcodeScanner/cppwinrt/BindingHelpers.h @@ -0,0 +1,37 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +#include "BindingHelpers.g.h" + +namespace winrt::SDKTemplate::implementation +{ + struct BindingHelpers + { + static bool Not(bool value) + { + return !value; + } + + static winrt::Windows::UI::Xaml::Visibility CollapsedIf(bool value) + { + return value ? winrt::Windows::UI::Xaml::Visibility::Collapsed : winrt::Windows::UI::Xaml::Visibility::Visible; + } + }; +} + +namespace winrt::SDKTemplate::factory_implementation +{ + struct BindingHelpers : BindingHelpersT + { + }; +} diff --git a/Samples/BarcodeScanner/cppwinrt/DataHelpers.cpp b/Samples/BarcodeScanner/cppwinrt/DataHelpers.cpp new file mode 100644 index 0000000000..384337b280 --- /dev/null +++ b/Samples/BarcodeScanner/cppwinrt/DataHelpers.cpp @@ -0,0 +1,84 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "pch.h" +#include +#include +#include "DataHelpers.h" + +namespace winrt +{ + using namespace Windows::Storage::Streams; + using namespace Windows::Devices::PointOfService; +} + +std::wstring GetDataString(winrt::IBuffer const& data) +{ + if (!data) + { + return L"No data"; + } + + // Just to show that we have the raw data, we'll print the value of the bytes. + // Arbitrarily limit the number of bytes printed so the UI isn't overloaded. + const unsigned int MAX_BYTES_TO_PRINT = 20; + unsigned int byteCount = (std::min)(data.Length(), MAX_BYTES_TO_PRINT); + + std::wstringstream buffer; + buffer << std::setfill(L'0') << std::hex; + for (uint32_t value : winrt::array_view(data.data(), byteCount)) + { + buffer << std::setw(2) << value << L" "; + } + + if (byteCount < data.Length()) + { + buffer << L"..."; + } + + return buffer.str(); +} + +winrt::hstring GetDataLabelString(winrt::IBuffer const& data, uint32_t scanDataType) +{ + // Only certain data types contain encoded text. + // To keep this simple, we'll just decode a few of them. + if (!data) + { + return L"No data"; + } + + if (scanDataType == winrt::BarcodeSymbologies::Upca() || + scanDataType == winrt::BarcodeSymbologies::UpcaAdd2() || + scanDataType == winrt::BarcodeSymbologies::UpcaAdd5() || + scanDataType == winrt::BarcodeSymbologies::Upce() || + scanDataType == winrt::BarcodeSymbologies::UpceAdd2() || + scanDataType == winrt::BarcodeSymbologies::UpceAdd5() || + scanDataType == winrt::BarcodeSymbologies::Ean8() || + scanDataType == winrt::BarcodeSymbologies::TfStd() || + scanDataType == winrt::BarcodeSymbologies::OcrA() || + scanDataType == winrt::BarcodeSymbologies::OcrB()) + { + // The UPC, EAN8, and 2 of 5 families encode the digits 0..9 + // which are then sent to the app in a UTF8 string (like "01234") + + // This is not an exhaustive list of symbologies that can be converted to a string + + winrt::DataReader reader = winrt::DataReader::FromBuffer(data); + return reader.ReadString(data.Length()); + } + else + { + // Some other symbologies (typically 2-D symbologies) contain binary data that + // should not be converted to text. + return hstring{ L"Decoded data unavailable. Raw label data: " + GetDataString(data) }; + } +} diff --git a/Samples/BarcodeScanner/cppwinrt/DataHelpers.h b/Samples/BarcodeScanner/cppwinrt/DataHelpers.h new file mode 100644 index 0000000000..b61310d91c --- /dev/null +++ b/Samples/BarcodeScanner/cppwinrt/DataHelpers.h @@ -0,0 +1,15 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +std::wstring GetDataString(winrt::Windows::Storage::Streams::IBuffer const& data); +winrt::hstring GetDataLabelString(winrt::Windows::Storage::Streams::IBuffer const& data, uint32_t scanDataType); diff --git a/Samples/BarcodeScanner/cppwinrt/Package.appxmanifest b/Samples/BarcodeScanner/cppwinrt/Package.appxmanifest new file mode 100644 index 0000000000..ab8de83491 --- /dev/null +++ b/Samples/BarcodeScanner/cppwinrt/Package.appxmanifest @@ -0,0 +1,45 @@ + + + + + + BarcodeScanner C++/WinRT Sample + Microsoft Corporation + Assets\storelogo-sdk.png + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/BarcodeScanner/cppwinrt/Project.idl b/Samples/BarcodeScanner/cppwinrt/Project.idl new file mode 100644 index 0000000000..383210f007 --- /dev/null +++ b/Samples/BarcodeScanner/cppwinrt/Project.idl @@ -0,0 +1,73 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +namespace SDKTemplate +{ + [default_interface] + runtimeclass Scenario1_BasicFunctionality : Windows.UI.Xaml.Controls.Page + { + Scenario1_BasicFunctionality(); + } + + [default_interface] + runtimeclass Scenario2_MultipleScanners : Windows.UI.Xaml.Controls.Page + { + Scenario2_MultipleScanners(); + } + + [default_interface] + runtimeclass Scenario3_ActiveSymbologies : Windows.UI.Xaml.Controls.Page + { + Scenario3_ActiveSymbologies(); + } + + [Windows.UI.Xaml.Data.Bindable] + runtimeclass SymbologyListEntry + { + SymbologyListEntry(UInt32 symbologyId); + SymbologyListEntry(UInt32 symbologyId, Boolean symbologyEnabled); + + UInt32 Id{ get; }; + Boolean IsEnabled; + String Name{ get; }; + } + + [default_interface] + runtimeclass Scenario4_SymbologyAttributes : Windows.UI.Xaml.Controls.Page + { + Scenario4_SymbologyAttributes(); + } + + runtimeclass Scenario5_DisplayingBarcodePreview : + Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged + { + Scenario5_DisplayingBarcodePreview(); + + Boolean IsScannerClaimed; + Boolean ScannerSupportsPreview; + Boolean IsPreviewing; + Boolean SoftwareTriggerStarted; + } + + static runtimeclass BindingHelpers + { + static Windows.UI.Xaml.Visibility CollapsedIf(Boolean value); + static Boolean Not(Boolean value); + } + + runtimeclass BarcodeScannerInfo + { + BarcodeScannerInfo(String name, String deviceId); + + String Name{ get; }; + String DeviceId{ get; }; + } +} diff --git a/Samples/BarcodeScanner/cppwinrt/SampleConfiguration.cpp b/Samples/BarcodeScanner/cppwinrt/SampleConfiguration.cpp new file mode 100644 index 0000000000..dc78e59dd1 --- /dev/null +++ b/Samples/BarcodeScanner/cppwinrt/SampleConfiguration.cpp @@ -0,0 +1,33 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "pch.h" +#include +#include "MainPage.h" +#include "SampleConfiguration.h" + +using namespace winrt; +using namespace winrt::Windows::Foundation::Collections; +using namespace winrt::SDKTemplate; + +hstring implementation::MainPage::FEATURE_NAME() +{ + return L"Barcode Scanner"; +} + +IVector implementation::MainPage::scenariosInner = winrt::single_threaded_observable_vector( +{ + Scenario{ L"DataReceived event", xaml_typename() }, + Scenario{ L"Release/Retain functionality", xaml_typename() }, + Scenario{ L"Active Symbologies", xaml_typename() }, + Scenario{ L"Symbology Attributes", xaml_typename() }, + Scenario{ L"Displaying a Barcode Preview", xaml_typename() }, +}); diff --git a/Samples/BarcodeScanner/cppwinrt/SampleConfiguration.h b/Samples/BarcodeScanner/cppwinrt/SampleConfiguration.h new file mode 100644 index 0000000000..93a2f26cbf --- /dev/null +++ b/Samples/BarcodeScanner/cppwinrt/SampleConfiguration.h @@ -0,0 +1,17 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once +#include "pch.h" + +namespace winrt::SDKTemplate +{ +} diff --git a/Samples/BarcodeScanner/cppwinrt/Scenario1_BasicFunctionality.cpp b/Samples/BarcodeScanner/cppwinrt/Scenario1_BasicFunctionality.cpp new file mode 100644 index 0000000000..e4e65b8db4 --- /dev/null +++ b/Samples/BarcodeScanner/cppwinrt/Scenario1_BasicFunctionality.cpp @@ -0,0 +1,152 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "pch.h" +#include +#include +#include "Scenario1_BasicFunctionality.h" +#include "Scenario1_BasicFunctionality.g.cpp" + +namespace winrt +{ + using namespace Windows::Foundation; + using namespace Windows::Devices; + using namespace Windows::Devices::Enumeration; + using namespace Windows::Devices::PointOfService; + using namespace Windows::UI::Xaml; + using namespace Windows::UI::Xaml::Navigation; +} + +namespace winrt::SDKTemplate::implementation +{ + Scenario1_BasicFunctionality::Scenario1_BasicFunctionality() + { + InitializeComponent(); + } + + void Scenario1_BasicFunctionality::OnNavigatedTo(NavigationEventArgs const&) + { + ResetTheScenarioState(); + } + + void Scenario1_BasicFunctionality::OnNavigatedFrom(NavigationEventArgs const&) + { + ResetTheScenarioState(); + } + + void Scenario1_BasicFunctionality::ResetTheScenarioState() + { + if (m_claimedScanner) + { + // Detach the event handlers + m_claimedScanner.DataReceived(m_claimedScannerDataReceivedToken); + m_claimedScanner.ReleaseDeviceRequested(m_claimedScannerReleaseDeviceRequestedToken); + // Release the Barcode Scanner and set to null + m_claimedScanner = nullptr; + } + + m_scanner = nullptr; + + // Reset the UI if we are still the current page. + if (Frame().Content() == *this) + { + m_rootPage.NotifyUser(L"Click the start scanning button to begin.", NotifyType::StatusMessage); + ScenarioOutputScanData().Text(L"No data"); + ScenarioOutputScanDataLabel().Text(L"No data"); + ScenarioOutputScanDataType().Text(L"No data"); + + // reset the button state + ScenarioEndScanButton().IsEnabled(false); + ScenarioStartScanButton().IsEnabled(true); + } + } + + fire_and_forget Scenario1_BasicFunctionality::ScenarioStartScanButton_Click(IInspectable const&, RoutedEventArgs const&) + { + auto lifetime = get_strong(); + ScenarioStartScanButton().IsEnabled(false); + + m_rootPage.NotifyUser(L"Acquiring barcode scanner object.", NotifyType::StatusMessage); + + // Obtain the default barcode scanner. + m_scanner = co_await winrt::BarcodeScanner::GetDefaultAsync(); + if (m_scanner) + { + // after successful creation, claim the scanner for exclusive use and enable it so that data reveived events are received. + m_claimedScanner = co_await m_scanner.ClaimScannerAsync(); + + if (m_claimedScanner) + { + // It is always a good idea to have a release device requested event handler. If this event is not handled, there are chances of another app can + // claim ownership of the barcode scanner. + m_claimedScannerReleaseDeviceRequestedToken = m_claimedScanner.ReleaseDeviceRequested({ get_weak(), &Scenario1_BasicFunctionality::claimedScanner_ReleaseDeviceRequested }); + + // after successfully claiming, attach the datareceived event handler. + m_claimedScannerDataReceivedToken = m_claimedScanner.DataReceived({ get_weak(), &Scenario1_BasicFunctionality::claimedScanner_DataReceived }); + + // Ask the API to decode the data by default. By setting this, API will decode the raw data from the barcode scanner and + // send the ScanDataLabel and ScanDataType in the DataReceived event + m_claimedScanner.IsDecodeDataEnabled(true); + + // enable the scanner. + // The scanner must be enabled in order to receive the DataReceived event. + co_await m_claimedScanner.EnableAsync(); + + m_rootPage.NotifyUser(L"Ready to scan. Device ID: " + m_scanner.DeviceId(), NotifyType::StatusMessage); + ScenarioEndScanButton().IsEnabled(true); + } + else + { + m_rootPage.NotifyUser(L"Claim barcode scanner failed.", NotifyType::ErrorMessage); + ScenarioStartScanButton().IsEnabled(true); + } + } + else + { + m_rootPage.NotifyUser(L"Barcode scanner not found. Please connect a barcode scanner.", NotifyType::ErrorMessage); + ScenarioStartScanButton().IsEnabled(true); + } + } + + /// + /// Event handler for the Release Device Requested event fired when barcode scanner receives Claim request from another application + /// + /// + /// Contains the ClamiedBarcodeScanner that is sending this request + void Scenario1_BasicFunctionality::claimedScanner_ReleaseDeviceRequested(IInspectable const&, ClaimedBarcodeScanner const& e) + { + // always retain the device + e.RetainDevice(); + + m_rootPage.NotifyUser(L"Event ReleaseDeviceRequested received. Retaining the barcode scanner.", NotifyType::StatusMessage); + } + + /// + /// Event handler for the DataReceived event fired when a barcode is scanned by the barcode scanner + /// + /// + /// Contains the BarcodeScannerReport which contains the data obtained in the scan + fire_and_forget Scenario1_BasicFunctionality::claimedScanner_DataReceived(ClaimedBarcodeScanner const&, BarcodeScannerDataReceivedEventArgs args) + { + auto lifetime = get_strong(); + co_await resume_foreground(Dispatcher()); + + // read the data from the buffer and convert to string. + ScenarioOutputScanDataLabel().Text(GetDataLabelString(args.Report().ScanDataLabel(), args.Report().ScanDataType())); + ScenarioOutputScanData().Text(GetDataString(args.Report().ScanData())); + ScenarioOutputScanDataType().Text(BarcodeSymbologies::GetName(args.Report().ScanDataType())); + } + + void Scenario1_BasicFunctionality::ScenarioEndScanButton_Click(IInspectable const&, RoutedEventArgs const&) + { + ResetTheScenarioState(); + } +} diff --git a/Samples/BarcodeScanner/cppwinrt/Scenario1_BasicFunctionality.h b/Samples/BarcodeScanner/cppwinrt/Scenario1_BasicFunctionality.h new file mode 100644 index 0000000000..560b3560cf --- /dev/null +++ b/Samples/BarcodeScanner/cppwinrt/Scenario1_BasicFunctionality.h @@ -0,0 +1,47 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +#include "Scenario1_BasicFunctionality.g.h" + +namespace winrt::SDKTemplate::implementation +{ + struct Scenario1_BasicFunctionality : Scenario1_BasicFunctionalityT + { + Scenario1_BasicFunctionality(); + + fire_and_forget ScenarioStartScanButton_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); + void ScenarioEndScanButton_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); + + void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs const& e); + void OnNavigatedFrom(Windows::UI::Xaml::Navigation::NavigationEventArgs const& e); + void claimedScanner_ReleaseDeviceRequested(Windows::Foundation::IInspectable const&, Windows::Devices::PointOfService::ClaimedBarcodeScanner const& e); + fire_and_forget claimedScanner_DataReceived(Windows::Devices::PointOfService::ClaimedBarcodeScanner const& sender, Windows::Devices::PointOfService::BarcodeScannerDataReceivedEventArgs args); + + private: + + void ResetTheScenarioState(); + + SDKTemplate::MainPage m_rootPage{ MainPage::Current() }; + Windows::Devices::PointOfService::BarcodeScanner m_scanner{ nullptr }; + Windows::Devices::PointOfService::ClaimedBarcodeScanner m_claimedScanner{ nullptr }; + event_token m_claimedScannerDataReceivedToken; + event_token m_claimedScannerReleaseDeviceRequestedToken; + }; +} + +namespace winrt::SDKTemplate::factory_implementation +{ + struct Scenario1_BasicFunctionality : Scenario1_BasicFunctionalityT + { + }; +} diff --git a/Samples/BarcodeScanner/cppwinrt/Scenario2_MultipleScanners.cpp b/Samples/BarcodeScanner/cppwinrt/Scenario2_MultipleScanners.cpp new file mode 100644 index 0000000000..25fad440a0 --- /dev/null +++ b/Samples/BarcodeScanner/cppwinrt/Scenario2_MultipleScanners.cpp @@ -0,0 +1,477 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "pch.h" + +#include "Scenario2_MultipleScanners.h" +#include "Scenario2_MultipleScanners.g.cpp" + +namespace winrt +{ + using namespace Windows::Devices; + using namespace Windows::Devices::Enumeration; + using namespace Windows::Devices::PointOfService; + using namespace Windows::Foundation; + using namespace Windows::UI; + using namespace Windows::UI::Xaml; + using namespace Windows::UI::Xaml::Controls; + using namespace Windows::UI::Xaml::Media; + using namespace Windows::UI::Xaml::Navigation; +} + +namespace winrt::SDKTemplate::implementation +{ + + Scenario2_MultipleScanners::Scenario2_MultipleScanners() + { + InitializeComponent(); + } + + /// + /// Invoked when this page is no longer displayed. + /// + /// Event data that describes how this page was exited. The Parameter + /// property is typically used to configure the page. + void Scenario2_MultipleScanners::OnNavigatedFrom(NavigationEventArgs const&) + { + ResetTheScenarioState(); + } + + /// + /// Invoked when this page is about to be displayed in a Frame. + /// + /// Event data that describes how this page was reached. The Parameter + /// property is typically used to configure the page. + void Scenario2_MultipleScanners::OnNavigatedTo(NavigationEventArgs const&) + { + ResetTheScenarioState(); + } + + /// + /// This is the click handler for the 'ScenarioStartScanningInstance1' button. It initiates creation of scanner instance 1. + /// + fire_and_forget Scenario2_MultipleScanners::ButtonStartScanningInstance1_Click(IInspectable const&, RoutedEventArgs const&) + { + auto lifetime = get_strong(); + + //Get the handle to the default scanner + if (co_await CreateDefaultScannerObjectAsync(BarcodeScannerInstance::Instance1)) + { + //Claim the scanner + if (co_await ClaimBarcodeScannerAsync(BarcodeScannerInstance::Instance1)) + { + //add the event handlers + m_dev1ReleaseRequestedToken = m_claimedBarcodeScannerInstance1.ReleaseDeviceRequested({ get_weak(), &Scenario2_MultipleScanners::claimedBarcodeScannerInstance1_ReleaseDeviceRequested }); + m_dev1DataReceivedToken = m_claimedBarcodeScannerInstance1.DataReceived({ get_weak(), &Scenario2_MultipleScanners::claimedBarcodeScannerInstance1_DataReceived }); + m_claimedBarcodeScannerInstance1.IsDecodeDataEnabled(true); + + //Enable the Scanner + if (co_await EnableBarcodeScannerAsync(BarcodeScannerInstance::Instance1)) + { + //Set the UI state + ResetUI(); + SetUI(BarcodeScannerInstance::Instance1); + } + } + else + { + if (m_barcodeScannerInstance1) + { + m_barcodeScannerInstance1.Close(); + m_barcodeScannerInstance1 = nullptr; + } + } + } + } + + /// + /// This method is called upon when a claim request is made on instance 1. If a retain request was placed on the device it rejects the new claim. + /// + fire_and_forget Scenario2_MultipleScanners::claimedBarcodeScannerInstance1_ReleaseDeviceRequested(IInspectable const&, ClaimedBarcodeScanner const&) + { + auto lifetime = get_strong(); + + co_await resume_foreground(Dispatcher()); + + //check if the instance wants to retain the device + if (Retain1().IsChecked().Value()) + { + try + { + //Retain the device + m_claimedBarcodeScannerInstance1.RetainDevice(); + } + catch (hresult_error const& exception) + { + m_rootPage.NotifyUser(L"Retain instance 1 failed: " + exception.message(), NotifyType::ErrorMessage); + } + } + //Release the device + else + { + m_claimedBarcodeScannerInstance1.Close(); + m_claimedBarcodeScannerInstance1 = nullptr; + + if (m_barcodeScannerInstance1) + { + m_barcodeScannerInstance1.Close(); + m_barcodeScannerInstance1 = nullptr; + } + } + } + + /// + /// This is the click handler for the 'ScenarioStartScanningInstance2' button. Initiates creation of scanner instance 2 + /// + fire_and_forget Scenario2_MultipleScanners::ButtonStartScanningInstance2_Click(IInspectable const&, RoutedEventArgs const&) + { + auto lifetime = get_strong(); + + //Get the handle to the default scanner + if (co_await CreateDefaultScannerObjectAsync(BarcodeScannerInstance::Instance2)) + { + //Claim the scanner + if (co_await ClaimBarcodeScannerAsync(BarcodeScannerInstance::Instance2)) + { + //set the handlers + m_dev2ReleaseRequestedToken = m_claimedBarcodeScannerInstance2.ReleaseDeviceRequested({ get_weak(), &Scenario2_MultipleScanners::claimedBarcodeScannerInstance2_ReleaseDeviceRequested }); + m_dev2DataReceivedToken = m_claimedBarcodeScannerInstance2.DataReceived({ get_weak(), &Scenario2_MultipleScanners::claimedBarcodeScannerInstance2_DataReceived }); + + //enable the scanner to decode the scanned data + m_claimedBarcodeScannerInstance2.IsDecodeDataEnabled(true); + + //Enable the Scanner + if (co_await EnableBarcodeScannerAsync(BarcodeScannerInstance::Instance2)) + { + //Set the UI state + ResetUI(); + SetUI(BarcodeScannerInstance::Instance2); + } + } + else + { + if (m_barcodeScannerInstance2) + { + m_barcodeScannerInstance2.Close(); + m_barcodeScannerInstance2 = nullptr; + } + } + } + } + + /// + /// This method is called upon when a claim request is made on instance 2. If a retain request was placed on the device it rejects the new claim. + /// + fire_and_forget Scenario2_MultipleScanners::claimedBarcodeScannerInstance2_ReleaseDeviceRequested(IInspectable const&, ClaimedBarcodeScanner const&) + { + auto lifetime = get_strong(); + + co_await resume_foreground(Dispatcher()); + + //check if the instance wants to retain the device + if (Retain2().IsChecked().Value()) + { + try + { + //Retain the device + m_claimedBarcodeScannerInstance2.RetainDevice(); + } + catch (hresult_error const& exception) + { + m_rootPage.NotifyUser(L"Retain instance 2 failed: " + exception.message(), NotifyType::ErrorMessage); + } + } + //Release the device + else + { + m_claimedBarcodeScannerInstance2.Close(); + m_claimedBarcodeScannerInstance2 = nullptr; + + if (m_barcodeScannerInstance2) + { + m_barcodeScannerInstance2.Close(); + m_barcodeScannerInstance2 = nullptr; + } + } + } + + /// + /// This is the click handler for the 'ScenarioEndScanningInstance1' button. + /// Initiates the disposal of scanner instance 1. + /// + void Scenario2_MultipleScanners::ButtonEndScanningInstance1_Click(IInspectable const&, RoutedEventArgs const&) + { + if (m_claimedBarcodeScannerInstance1) + { + //remove the event handlers + m_claimedBarcodeScannerInstance1.ReleaseDeviceRequested(m_dev1ReleaseRequestedToken); + m_claimedBarcodeScannerInstance1.DataReceived(m_dev1DataReceivedToken); + + //dispose the instance + m_claimedBarcodeScannerInstance1.Close(); + m_claimedBarcodeScannerInstance1 = nullptr; + } + + if (m_barcodeScannerInstance1) + { + m_barcodeScannerInstance1.Close(); + m_barcodeScannerInstance1 = nullptr; + } + + //reset the UI + + ResetUI(); + + m_rootPage.NotifyUser(L"Click a start scanning button to begin.", NotifyType::StatusMessage); + } + + /// + /// This is the click handler for the 'ScenarioEndScanningInstance2' button. + /// Initiates the disposal fo scanner instance 2. + /// + void Scenario2_MultipleScanners::ButtonEndScanningInstance2_Click(IInspectable const&, RoutedEventArgs const&) + { + if (m_claimedBarcodeScannerInstance2) + { + //remove the event handlers + m_claimedBarcodeScannerInstance2.ReleaseDeviceRequested(m_dev2ReleaseRequestedToken); + m_claimedBarcodeScannerInstance2.DataReceived(m_dev2DataReceivedToken); + + //dispose the instance + m_claimedBarcodeScannerInstance2.Close(); + m_claimedBarcodeScannerInstance2 = nullptr; + } + + if (m_barcodeScannerInstance2) + { + m_barcodeScannerInstance2.Close(); + m_barcodeScannerInstance2 = nullptr; + } + + //reset the UI + ResetUI(); + + m_rootPage.NotifyUser(L"Click a start scanning button to begin.", NotifyType::StatusMessage); + } + + /// + /// This method returns the first available Barcode Scanner. To enumerate and find a particular device use the device enumeration code. + /// + /// a boolean value based on whether it found a compatible scanner connected + + IAsyncOperation Scenario2_MultipleScanners::CreateDefaultScannerObjectAsync(BarcodeScannerInstance instance) + { + auto lifetime = get_strong(); + + BarcodeScanner scanner = co_await BarcodeScanner::GetDefaultAsync(); + + if (!scanner) + { + m_rootPage.NotifyUser(L"Barcode scanner not found. Please connect a barcode scanner.", NotifyType::ErrorMessage); + co_return false; + } + + switch (instance) + { + case BarcodeScannerInstance::Instance1: + m_barcodeScannerInstance1 = scanner; + break; + case BarcodeScannerInstance::Instance2: + m_barcodeScannerInstance2 = scanner; + break; + default: + co_return false; + } + + co_return true; + } + + /// + /// This method claims the connected scanner. + /// + /// a boolean based on whether it was able to claim the scanner. + + IAsyncOperation Scenario2_MultipleScanners::ClaimBarcodeScannerAsync(BarcodeScannerInstance instance) + { + auto lifetime = get_strong(); + bool claimAsyncStatus = false; + //select the instance to claim + switch (instance) + { + case BarcodeScannerInstance::Instance1: + + m_claimedBarcodeScannerInstance1 = co_await m_barcodeScannerInstance1.ClaimScannerAsync(); + if (!m_claimedBarcodeScannerInstance1) + m_rootPage.NotifyUser(L"Instance 1 claim barcode scanner failed.", NotifyType::ErrorMessage); + else + claimAsyncStatus = true; + break; + + case BarcodeScannerInstance::Instance2: + + m_claimedBarcodeScannerInstance2 = co_await m_barcodeScannerInstance2.ClaimScannerAsync(); + if (!m_claimedBarcodeScannerInstance2) + m_rootPage.NotifyUser(L"Instance 2 claim barcode scanner failed.", NotifyType::ErrorMessage); + else + claimAsyncStatus = true; + break; + + default: + co_return claimAsyncStatus; + } + co_return claimAsyncStatus; + } + + /// + /// This method enables the connected scanner. + /// + /// a boolean based on whether it was able to enable the scanner. + + IAsyncOperation Scenario2_MultipleScanners::EnableBarcodeScannerAsync(BarcodeScannerInstance instance) + { + auto lifetime = get_strong(); + switch (instance) + { + case BarcodeScannerInstance::Instance1: + co_await m_claimedBarcodeScannerInstance1.EnableAsync(); + m_rootPage.NotifyUser(L"Instance 1 ready to scan. Device ID: " + m_claimedBarcodeScannerInstance1.DeviceId(), NotifyType::StatusMessage); + break; + case BarcodeScannerInstance::Instance2: + co_await m_claimedBarcodeScannerInstance2.EnableAsync(); + m_rootPage.NotifyUser(L"Instance 2 ready to scan. Device ID: " + m_claimedBarcodeScannerInstance2.DeviceId(), NotifyType::StatusMessage); + break; + default: + co_return false; + } + co_return true; + } + + /// + /// Reset the Scenario state + /// + + void Scenario2_MultipleScanners::ResetTheScenarioState() + { + if (m_claimedBarcodeScannerInstance1) + { + m_claimedBarcodeScannerInstance1.Close(); + m_claimedBarcodeScannerInstance1 = nullptr; + } + + if (m_barcodeScannerInstance1) + { + m_barcodeScannerInstance1.Close(); + m_barcodeScannerInstance1 = nullptr; + } + + if (m_claimedBarcodeScannerInstance2) + { + m_claimedBarcodeScannerInstance2.Close(); + m_claimedBarcodeScannerInstance2 = nullptr; + } + + if (m_barcodeScannerInstance2) + { + m_barcodeScannerInstance2.Close(); + m_barcodeScannerInstance2 = nullptr; + } + + ResetUI(); + } + + /// + /// Resets the display Elements to original state + /// + + void Scenario2_MultipleScanners::ResetUI() + { + Instance1Border().BorderBrush(SolidColorBrush{ Colors::Gray() }); + Instance2Border().BorderBrush(SolidColorBrush{ Colors::Gray() }); + + ScanDataType1().Text(L"No data"); + ScanData1().Text(L"No data"); + DataLabel1().Text(L"No data"); + ScanDataType2().Text(L"No data"); + ScanData2().Text(L"No data"); + DataLabel2().Text(L"No data"); + + ScenarioStartScanningInstance1().IsEnabled(true); + ScenarioStartScanningInstance2().IsEnabled(true); + ScenarioEndScanningInstance1().IsEnabled(false); + ScenarioEndScanningInstance2().IsEnabled(false); + } + + /// + /// Sets the UI elements to a state corresponding to the current active Instance. + /// + /// Corresponds to the current active instance + + void Scenario2_MultipleScanners::SetUI(BarcodeScannerInstance instance) + { + Instance1Border().BorderBrush(SolidColorBrush{ Colors::Gray() }); + Instance2Border().BorderBrush(SolidColorBrush{ Colors::Gray() }); + + switch (instance) + { + case BarcodeScannerInstance::Instance1: + ScenarioStartScanningInstance1().IsEnabled(false); + ScenarioStartScanningInstance2().IsEnabled(true); + ScenarioEndScanningInstance1().IsEnabled(true); + ScenarioEndScanningInstance2().IsEnabled(false); + Instance1Border().BorderBrush(SolidColorBrush{ Colors::DarkBlue() }); + break; + + case BarcodeScannerInstance::Instance2: + ScenarioStartScanningInstance1().IsEnabled(true); + ScenarioStartScanningInstance2().IsEnabled(false); + ScenarioEndScanningInstance1().IsEnabled(false); + ScenarioEndScanningInstance2().IsEnabled(true); + Instance2Border().BorderBrush(SolidColorBrush{ Colors::DarkBlue() }); + break; + } + } + + /// + /// This is an event handler for the claimed scanner Instance 1 when it scans and recieves data + /// + /// + /// + + fire_and_forget Scenario2_MultipleScanners::claimedBarcodeScannerInstance1_DataReceived(ClaimedBarcodeScanner const&, BarcodeScannerDataReceivedEventArgs args) + { + auto lifetime = get_strong(); + co_await resume_foreground(Dispatcher()); + + ScanDataType1().Text(BarcodeSymbologies::GetName(args.Report().ScanDataType())); + DataLabel1().Text(GetDataLabelString(args.Report().ScanDataLabel(), args.Report().ScanDataType())); + ScanData1().Text(GetDataString(args.Report().ScanData())); + + m_rootPage.NotifyUser(L"Instance 1 received data from the barcode scanner.", NotifyType::StatusMessage); + } + + /// + /// This is an event handler for the claimed scanner Instance 2 when it scans and recieves data + /// + /// + /// + + fire_and_forget Scenario2_MultipleScanners::claimedBarcodeScannerInstance2_DataReceived(ClaimedBarcodeScanner const&, BarcodeScannerDataReceivedEventArgs args) + { + auto lifetime = get_strong(); + co_await resume_foreground(Dispatcher()); + ScanDataType2().Text(BarcodeSymbologies::GetName(args.Report().ScanDataType())); + DataLabel2().Text(GetDataLabelString(args.Report().ScanDataLabel(), args.Report().ScanDataType())); + ScanData2().Text(GetDataString(args.Report().ScanData())); + + m_rootPage.NotifyUser(L"Instance 2 received data from the barcode scanner.", NotifyType::StatusMessage); + } + +} \ No newline at end of file diff --git a/Samples/BarcodeScanner/cppwinrt/Scenario2_MultipleScanners.h b/Samples/BarcodeScanner/cppwinrt/Scenario2_MultipleScanners.h new file mode 100644 index 0000000000..ceaf264c5d --- /dev/null +++ b/Samples/BarcodeScanner/cppwinrt/Scenario2_MultipleScanners.h @@ -0,0 +1,74 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +#include "Scenario2_MultipleScanners.g.h" + +namespace winrt::SDKTemplate::implementation +{ + struct Scenario2_MultipleScanners : Scenario2_MultipleScannersT + { + Scenario2_MultipleScanners(); + + void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs const& e); + void OnNavigatedFrom(Windows::UI::Xaml::Navigation::NavigationEventArgs const& e); + + fire_and_forget ButtonStartScanningInstance1_Click(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::RoutedEventArgs const&); + fire_and_forget claimedBarcodeScannerInstance1_ReleaseDeviceRequested(Windows::Foundation::IInspectable const& sender, Windows::Devices::PointOfService::ClaimedBarcodeScanner const&); + fire_and_forget ButtonStartScanningInstance2_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const&); + fire_and_forget claimedBarcodeScannerInstance2_ReleaseDeviceRequested(Windows::Foundation::IInspectable const& sender, Windows::Devices::PointOfService::ClaimedBarcodeScanner const&); + + void ButtonEndScanningInstance1_Click(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::RoutedEventArgs const&); + void ButtonEndScanningInstance2_Click(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::RoutedEventArgs const&); + + private: + SDKTemplate::MainPage m_rootPage{ SDKTemplate::MainPage::Current() }; + + Windows::Devices::PointOfService::BarcodeScanner m_barcodeScannerInstance1{ nullptr }; + Windows::Devices::PointOfService::BarcodeScanner m_barcodeScannerInstance2{ nullptr }; + Windows::Devices::PointOfService::ClaimedBarcodeScanner m_claimedBarcodeScannerInstance1{ nullptr }; + Windows::Devices::PointOfService::ClaimedBarcodeScanner m_claimedBarcodeScannerInstance2{ nullptr }; + event_token m_dev1ReleaseRequestedToken; + event_token m_dev1DataReceivedToken; + event_token m_dev2ReleaseRequestedToken; + event_token m_dev2DataReceivedToken; + + /// + /// Enumerator for current active Instance. + /// + enum class BarcodeScannerInstance + { + Instance1, + Instance2 + }; + + Windows::Foundation::IAsyncOperation CreateDefaultScannerObjectAsync(BarcodeScannerInstance instance); + Windows::Foundation::IAsyncOperation ClaimBarcodeScannerAsync(BarcodeScannerInstance instance); + Windows::Foundation::IAsyncOperation EnableBarcodeScannerAsync(BarcodeScannerInstance instance); + + void ResetTheScenarioState(); + void ResetUI(); + + void SetUI(BarcodeScannerInstance instance); + + fire_and_forget claimedBarcodeScannerInstance1_DataReceived(Windows::Devices::PointOfService::ClaimedBarcodeScanner const&, Windows::Devices::PointOfService::BarcodeScannerDataReceivedEventArgs args); + fire_and_forget claimedBarcodeScannerInstance2_DataReceived(Windows::Devices::PointOfService::ClaimedBarcodeScanner const&, Windows::Devices::PointOfService::BarcodeScannerDataReceivedEventArgs args); + + }; +} + +namespace winrt::SDKTemplate::factory_implementation +{ + struct Scenario2_MultipleScanners : Scenario2_MultipleScannersT + { + }; +} diff --git a/Samples/BarcodeScanner/cppwinrt/Scenario3_ActiveSymbologies.cpp b/Samples/BarcodeScanner/cppwinrt/Scenario3_ActiveSymbologies.cpp new file mode 100644 index 0000000000..0fb8d200e8 --- /dev/null +++ b/Samples/BarcodeScanner/cppwinrt/Scenario3_ActiveSymbologies.cpp @@ -0,0 +1,205 @@ +#include "pch.h" +#include "Scenario3_ActiveSymbologies.h" +#include "Scenario3_ActiveSymbologies.g.cpp" + +namespace winrt +{ + using namespace Windows::Foundation; + using namespace Windows::Foundation::Collections; + using namespace Windows::Devices::PointOfService; + using namespace Windows::UI::Core; + using namespace Windows::UI::Xaml; + using namespace Windows::UI::Xaml::Data; + using namespace Windows::UI::Xaml::Controls; + using namespace Windows::UI::Xaml::Navigation; +} + +namespace winrt::SDKTemplate::implementation +{ + Scenario3_ActiveSymbologies::Scenario3_ActiveSymbologies() + { + InitializeComponent(); + + m_listOfSymbologies = single_threaded_observable_vector(); + SymbologyListSource().Source(m_listOfSymbologies); + } + + /// + /// Invoked when this page is about to be displayed in a Frame. + /// + void Scenario3_ActiveSymbologies::OnNavigatedTo(NavigationEventArgs const&) + { + ResetTheScenarioState(); + } + + /// + /// Invoked when this page is no longer displayed. + /// + void Scenario3_ActiveSymbologies::OnNavigatedFrom(NavigationEventArgs const&) + { + ResetTheScenarioState(); + } + + /// + /// Event Handler for Start Scan Button Click. + /// Sets up the barcode scanner to be ready to receive the data events from the scan. + /// + fire_and_forget Scenario3_ActiveSymbologies::ScenarioStartScanButton_Click(IInspectable const&, RoutedEventArgs const&) + { + auto lifetime = get_strong(); + + ScenarioStartScanButton().IsEnabled(false); + + m_rootPage.NotifyUser(L"Acquiring barcode scanner object.", NotifyType::StatusMessage); + + // create the barcode scanner. + m_scanner = co_await BarcodeScanner::GetDefaultAsync(); + + if (m_scanner) + { + // Claim the scanner for exclusive use and enable it so raises DataReceived events. + m_claimedScanner = co_await m_scanner.ClaimScannerAsync(); + if (m_claimedScanner) + { + // It is always a good idea to have a release device requested event handler. + // If this event is not handled, then another app can claim ownership of the barcode scanner. + m_scannerReleaseRequestedToken = m_claimedScanner.ReleaseDeviceRequested({ get_weak(), &Scenario3_ActiveSymbologies::claimedScanner_ReleaseDeviceRequested }); + + // after successfully claiming, attach the datareceived event handler. + m_scannerDataReceivedToken = m_claimedScanner.DataReceived({ get_weak(), &Scenario3_ActiveSymbologies::claimedScanner_DataReceived }); + + // Ask the platform to decode the data by default. When this is set, the platform + // will decode the raw data from the barcode scanner and include in the + // BarcodeScannerReport.ScanDataLabel and ScanDataType in the DataReceived event. + m_claimedScanner.IsDecodeDataEnabled(true); + + // Enable the scanner so it raises DataReceived events. + // Do this after adding the DataReceived event handler. + co_await m_claimedScanner.EnableAsync(); + + // after successful claim, list supported symbologies + auto supportedSymbologies = co_await m_scanner.GetSupportedSymbologiesAsync(); + for (uint32_t symbology : supportedSymbologies) + { + m_listOfSymbologies.Append(SymbologyListEntry{ symbology }); + } + + // reset the button state + ScenarioEndScanButton().IsEnabled(true); + SetActiveSymbologiesButton().IsEnabled(true); + + m_rootPage.NotifyUser(L"Ready to scan. Device ID: " + m_claimedScanner.DeviceId(), NotifyType::StatusMessage); + } + else + { + m_scanner.Close(); + m_scanner = nullptr; + ScenarioStartScanButton().IsEnabled(true); + m_rootPage.NotifyUser(L"Claim barcode scanner failed.", NotifyType::ErrorMessage); + } + } + else + { + ScenarioStartScanButton().IsEnabled(true); + m_rootPage.NotifyUser(L"Barcode scanner not found. Please connect a barcode scanner.", NotifyType::ErrorMessage); + } + } + + /// + /// Event handler for the Release Device Requested event fired when barcode scanner receives Claim request from another application + /// + /// Contains the ClamiedBarcodeScanner that is sending this request + void Scenario3_ActiveSymbologies::claimedScanner_ReleaseDeviceRequested(IInspectable const&, ClaimedBarcodeScanner const& e) + { + // always retain the device + e.RetainDevice(); + m_rootPage.NotifyUser(L"Event ReleaseDeviceRequested received. Retaining the barcode scanner.", NotifyType::StatusMessage); + } + + + /// + /// Event handler for the DataReceived event fired when a barcode is scanned by the barcode scanner + /// + /// Contains the BarcodeScannerDataReceivedEventArgs which contains the data obtained in the scan + fire_and_forget Scenario3_ActiveSymbologies::claimedScanner_DataReceived(ClaimedBarcodeScanner const&, BarcodeScannerDataReceivedEventArgs args) + { + auto lifetime = get_strong(); + + // need to update the UI data on the dispatcher thread. + // update the UI with the data received from the scan. + co_await resume_foreground(Dispatcher()); + + // read the data from the buffer and convert to string. + ScenarioOutputScanDataLabel().Text(GetDataLabelString(args.Report().ScanDataLabel(), args.Report().ScanDataType())); + ScenarioOutputScanData().Text(GetDataString(args.Report().ScanData())); + ScenarioOutputScanDataType().Text(BarcodeSymbologies::GetName(args.Report().ScanDataType())); + } + + /// + /// Reset the Scenario state + /// + void Scenario3_ActiveSymbologies::ResetTheScenarioState() + { + if (m_claimedScanner) + { + // Detach the event handlers + m_claimedScanner.DataReceived(m_scannerDataReceivedToken); + m_claimedScanner.ReleaseDeviceRequested(m_scannerReleaseRequestedToken); + + // Release the Barcode Scanner and set to nullptr + m_claimedScanner.Close(); + m_claimedScanner = nullptr; + } + + if (m_scanner) + { + m_scanner.Close(); + m_scanner = nullptr; + } + + // Reset the UI if we are still the current page. + if (Frame().Content() == *this) + { + m_rootPage.NotifyUser(L"Click the start scanning button to begin.", NotifyType::StatusMessage); + ScenarioOutputScanData().Text(L"No data"); + ScenarioOutputScanDataLabel().Text(L"No data"); + ScenarioOutputScanDataType().Text(L"No data"); + + // reset the button state + SetActiveSymbologiesButton().IsEnabled(false); + ScenarioEndScanButton().IsEnabled(false); + ScenarioStartScanButton().IsEnabled(true); + + // reset symbology list + m_listOfSymbologies.Clear(); + } + } + + /// + /// Event handler for End Scan Button Click. + /// Releases the Barcode Scanner and resets the text in the UI + /// + void Scenario3_ActiveSymbologies::ScenarioEndScanButton_Click(IInspectable const&, RoutedEventArgs const&) + { + ResetTheScenarioState(); + } + + fire_and_forget Scenario3_ActiveSymbologies::SetActiveSymbologies_Click(IInspectable const&, RoutedEventArgs const&) + { + auto lifetime = get_strong(); + + if (m_claimedScanner) + { + std::vector symbologyList; + for (auto&& symbologyListEntry : m_listOfSymbologies) + { + if (symbologyListEntry.IsEnabled()) + { + symbologyList.push_back(symbologyListEntry.Id()); + } + } + + co_await m_claimedScanner.SetActiveSymbologiesAsync(std::move(symbologyList)); + } + } +} \ No newline at end of file diff --git a/Samples/BarcodeScanner/cppwinrt/Scenario3_ActiveSymbologies.h b/Samples/BarcodeScanner/cppwinrt/Scenario3_ActiveSymbologies.h new file mode 100644 index 0000000000..17d6c2e9b8 --- /dev/null +++ b/Samples/BarcodeScanner/cppwinrt/Scenario3_ActiveSymbologies.h @@ -0,0 +1,51 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +#include "Scenario3_ActiveSymbologies.g.h" + +namespace winrt::SDKTemplate::implementation +{ + struct Scenario3_ActiveSymbologies : Scenario3_ActiveSymbologiesT + { + Scenario3_ActiveSymbologies(); + + void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs const&); + void OnNavigatedFrom(Windows::UI::Xaml::Navigation::NavigationEventArgs const&); + + fire_and_forget ScenarioStartScanButton_Click(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::RoutedEventArgs const&); + fire_and_forget SetActiveSymbologies_Click(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::RoutedEventArgs const&); + void ScenarioEndScanButton_Click(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::RoutedEventArgs const&); + + private: + SDKTemplate::MainPage m_rootPage{ MainPage::Current() }; + Windows::Devices::PointOfService::BarcodeScanner m_scanner{ nullptr }; + Windows::Devices::PointOfService::ClaimedBarcodeScanner m_claimedScanner{ nullptr }; + + Windows::Foundation::Collections::IObservableVector m_listOfSymbologies{ nullptr }; + + event_token m_scannerReleaseRequestedToken; + event_token m_scannerDataReceivedToken; + + void claimedScanner_ReleaseDeviceRequested(Windows::Foundation::IInspectable const&, Windows::Devices::PointOfService::ClaimedBarcodeScanner const& e); + fire_and_forget claimedScanner_DataReceived(Windows::Devices::PointOfService::ClaimedBarcodeScanner const&, Windows::Devices::PointOfService::BarcodeScannerDataReceivedEventArgs args); + + void ResetTheScenarioState(); + }; +} + +namespace winrt::SDKTemplate::factory_implementation +{ + struct Scenario3_ActiveSymbologies : Scenario3_ActiveSymbologiesT + { + }; +} diff --git a/Samples/BarcodeScanner/cppwinrt/Scenario3_ActiveSymbologies.xaml b/Samples/BarcodeScanner/cppwinrt/Scenario3_ActiveSymbologies.xaml new file mode 100644 index 0000000000..46399c6d07 --- /dev/null +++ b/Samples/BarcodeScanner/cppwinrt/Scenario3_ActiveSymbologies.xaml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + Lists supported symbologies for the device to illustrate the use of setting active symbologiges functionality. Check or uncheck symbologies and tap "Set Active Symbologies" button to set active symbologies. + + + + + + + + diff --git a/Samples/Capabilities/shared/Scenario2_RequestMany.xaml b/Samples/Capabilities/shared/Scenario2_RequestMany.xaml new file mode 100644 index 0000000000..77b4db0d1a --- /dev/null +++ b/Samples/Capabilities/shared/Scenario2_RequestMany.xaml @@ -0,0 +1,31 @@ + + + + + + + Requesting capabilities + + + An app can request access to multiple capabilities at once. + + + + diff --git a/Samples/HolographicTagAlong/cpp/Content/QuadRenderer.cpp b/Samples/HolographicTagAlong/cpp/Content/QuadRenderer.cpp index bf162f4ef1..9fc661a2c8 100644 --- a/Samples/HolographicTagAlong/cpp/Content/QuadRenderer.cpp +++ b/Samples/HolographicTagAlong/cpp/Content/QuadRenderer.cpp @@ -80,20 +80,21 @@ void QuadRenderer::Update(const DX::StepTimer& timer) const float oneOverDeltaTime = 1.f / deltaTime; // Create a direction normal from the hologram's position to the origin of person space. - // This is the z-axis rotation. + // This is the z-axis of our rotated coordinate system in unrotated space. XMVECTOR facingNormal = XMVector3Normalize(-XMLoadFloat3(&m_position)); // Rotate the x-axis around the y-axis. - // This is a 90-degree angle from the normal, in the xz-plane. - // This is the x-axis rotation. + // This rotation is achieved by projecting the normal into the xz-plane. + // This is the x-axis of our rotated coordinate system in unrotated space. XMVECTOR xAxisRotation = XMVector3Normalize(XMVectorSet(XMVectorGetZ(facingNormal), 0.f, -XMVectorGetX(facingNormal), 0.f)); // Create a third normal to satisfy the conditions of a rotation matrix. - // The cross product of the other two normals is at a 90-degree angle to - // both normals. (Normalize the cross product to avoid floating-point math - // errors.) - // Note how the cross product will never be a zero-matrix because the two normals - // are always at a 90-degree angle from one another. + // The cross product of the other two normals is at a 90-degree angle to + // both normals. The cross product is normalized to avoid floating-point + // math errors. + // Note how the cross product will never be null because the two axis + // normals we generated above will always be at a 90-degree angle from + // one another. XMVECTOR yAxisRotation = XMVector3Normalize(XMVector3Cross(facingNormal, xAxisRotation)); // Construct the 4x4 rotation matrix. diff --git a/Samples/Logging/README.md b/Samples/Logging/README.md index 5114c9ab0e..3b3cf21f86 100644 --- a/Samples/Logging/README.md +++ b/Samples/Logging/README.md @@ -4,6 +4,7 @@ languages: - csharp - cpp - cppcx +- cppwinrt products: - windows - windows-uwp diff --git a/Samples/Logging/cppwinrt/FileLoggingSessionScenario.cpp b/Samples/Logging/cppwinrt/FileLoggingSessionScenario.cpp new file mode 100644 index 0000000000..2ff6b2bc80 --- /dev/null +++ b/Samples/Logging/cppwinrt/FileLoggingSessionScenario.cpp @@ -0,0 +1,399 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "pch.h" +#include "FileLoggingSessionScenario.h" +#include "LoggingScenarioHelpers.h" + +#define SCENARIO_PREFIX L"FileLoggingSessionScenario_" +#define DEFAULT_SESSION_NAME SCENARIO_PREFIX L"Session" +#define DEFAULT_CHANNEL_NAME SCENARIO_PREFIX L"Channel" +#define OUR_SAMPLE_APP_LOG_FILE_FOLDER_NAME SCENARIO_PREFIX L"LogFiles" +#define LOGGING_ENABLED_SETTING_KEY_NAME SCENARIO_PREFIX L"LoggingEnabled" +#define LOGFILEGEN_BEFORE_SUSPEND_SETTING_KEY_NAME SCENARIO_PREFIX L"LogFileGeneratedBeforeSuspend" + +using namespace winrt; +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::Foundation::Diagnostics; +using namespace winrt::Windows::Storage; +using namespace winrt::SDKTemplate; + +FileLoggingSessionScenario::~FileLoggingSessionScenario() +{ +} + +FileLoggingSessionScenario::FileLoggingSessionScenario() +{ + // Create the logging channel. + // When an app logs messages to a channel, the messges will go + // to any sessions which are consuming messages from the channel. + _channel = LoggingChannel(DEFAULT_CHANNEL_NAME); + + // This sample listens to the channel's events so it can track + // information about the channel's enablement status. + _channel.LoggingEnabled({ this, &FileLoggingSessionScenario::OnChannelLoggingEnabled }); + + // Attach event handlers for Suspend/Resume. + Windows::UI::Xaml::Application app = Windows::UI::Xaml::Application::Current(); + app.Suspending({ this, &FileLoggingSessionScenario::OnAppSuspending }); + app.Resuming({ this, &FileLoggingSessionScenario::OnAppResuming }); + + // If the app is being launched (not resumed), the following call will + // activate logging if it had been active at the time of the last suspend. + ResumeLoggingIfApplicable(); +} + +// static +FileLoggingSessionScenario& +FileLoggingSessionScenario::Instance() +{ + static FileLoggingSessionScenario instance; + return instance; +} + +event_token +FileLoggingSessionScenario::StatusChanged(StatusChangedHandler const& handler) +{ + return _statusChanged.add(handler); +} + +void +FileLoggingSessionScenario::StatusChanged(event_token const& token) noexcept +{ + _statusChanged.remove(token); +} + +bool +FileLoggingSessionScenario::IsBusy() const noexcept +{ + return _isBusy != 0; +} + +int +FileLoggingSessionScenario::LogFileGeneratedCount() const noexcept +{ + return _logFileGeneratedCount; +} + +bool +FileLoggingSessionScenario::LoggingEnabled() const noexcept +{ + return _session != nullptr; +} + +// Handle a request to toggle logging enable/disable status. +IAsyncOperation +FileLoggingSessionScenario::ToggleLoggingEnabledDisabledAsync() +{ + BusySetter busy(*this); + + if (_session != nullptr) + { + // Save any final log file. + StorageFile finalLogFile = co_await CloseSessionSaveFinalLogFile(); + + // Cleanup the session and let the sample UI know of the new status. + _session.Close(); + _session = nullptr; + _statusChanged(make(LoggingScenarioEventType::LogFileGeneratedAtDisable, finalLogFile == nullptr ? L"" : finalLogFile.Path())); + _settings.Insert(LOGGING_ENABLED_SETTING_KEY_NAME, box_value(false)); + _statusChanged(make(LoggingScenarioEventType::LoggingEnabledDisabled, false)); + co_return false; + } + else + { + // Enable logging and let the UI know of the new status. + StartLogging(); + _settings.Insert(LOGGING_ENABLED_SETTING_KEY_NAME, box_value(true)); + _statusChanged(make(LoggingScenarioEventType::LoggingEnabledDisabled, true)); + co_return true; + } +} + +// DoScenarioAsync creates a worker thread to simulate application activity. +// In actual use, logging functions would be called from various parts of a +// larger application. The purpose of the below concise function is to show +// how logging works over periods of time when lots of messages are logged. +// Primarily, it shows how log files are generated when each log file reaches +// its maximum capacity. This function logs information in a loop +// that exits after 3 log files have been generated. After each log file is +// generated, the sample's UI is updated with the log file's name. +IAsyncAction +FileLoggingSessionScenario::DoScenarioAsync() +{ + // Tell the UI the scenario is busy working. + BusySetter busy(*this); + + std::wstring messageToLog; + + int messageIndex = 0; // Increments for each logging call. + int startFileCount = LogFileGeneratedCount(); + + const int NUMBER_OF_LOG_FILES_TO_GENERATE = 3; + + // To demo logging, loop and log messages until 3 log files have been produced. + // Each time a new log is created, the FileLoggingSession will notify this sample + // via the FileLoggingSession::LogFileGenerated event. + // While not 3 files yet, execute the following code... + while (LogFileGeneratedCount() - startFileCount < NUMBER_OF_LOG_FILES_TO_GENERATE) + { + // Since the channel is added to the session at level Warning, the following + // message is logged because it is logged at level LoggingLevel.Critical. + messageToLog = L"Message="; + messageToLog += std::to_wstring(++messageIndex); + messageToLog += + L": Lorem ipsum dolor sit amet, consectetur adipiscing elit.In ligula nisi, vehicula nec eleifend vel, rutrum non dolor.Vestibulum ante ipsum " + L"primis in faucibus orci luctus et ultrices posuere cubilia Curae; Curabitur elementum scelerisque accumsan. In hac habitasse platea dictumst."; + _channel.LogMessage(messageToLog, LoggingLevel::Critical); + + // Since the channel is added to the session at level Warning, the following + // message is *not* logged because it is logged at LoggingLevel.Information. + messageToLog = L"Message="; + messageToLog += std::to_wstring(++messageIndex); + messageToLog += + L": Lorem ipsum dolor sit amet, consectetur adipiscing elit.In ligula nisi, vehicula nec eleifend vel, rutrum non dolor.Vestibulum ante ipsum " + L"primis in faucibus orci luctus et ultrices posuere cubilia Curae; Curabitur elementum scelerisque accumsan. In hac habitasse platea dictumst."; + _channel.LogMessage(messageToLog, LoggingLevel::Information); + + int value = 1000000; // one million, 7 digits, 4-bytes as an int, 14 bytes as a wide character string. + messageToLog = L"Value #"; + messageToLog += std::to_wstring(++messageIndex); + messageToLog += L':'; + messageToLog += std::to_wstring(value); + _channel.LogMessage(messageToLog, LoggingLevel::Critical); // 'value' is logged as 14 byte wide character string. + ++messageIndex; + _channel.LogValuePair(L"Value", value, LoggingLevel::Critical); // 'value' is logged as a 4-byte integer. + + // + // Pause every once in a while to simulate application activity outside of logging. + // + + if (messageIndex % 50 == 0) + { + co_await resume_after(std::chrono::milliseconds(5)); + } + } +} + +void +FileLoggingSessionScenario::StartLogging() +{ + // If no session exists, create one. + // NOTE: There are use cases where an application + // may want to create only a channel for sessions outside + // of the application itself. See MSDN for details. This + // sample is the common scenario of an app logging events + // which it wants to place in its own log file, so it creates + // a session and channel as a pair. The channel is created + // during construction of this LoggingScenario class so + // it already exsits by the time this function is called. + if (_session == nullptr) + { + _session = FileLoggingSession(DEFAULT_SESSION_NAME); + _session.LogFileGenerated({ this, &FileLoggingSessionScenario::OnLogFileGenerated }); // event+=handler + } + + // This sample adds the channel at level "warning" to + // demonstrated how messages logged at more verbose levels + // are ignored by the session. + _session.AddLoggingChannel(_channel, LoggingLevel::Warning); +} + +// Prepare this scenario for suspend. +IAsyncAction +FileLoggingSessionScenario::PrepareToSuspendAsync() +{ + if (_session != nullptr) + { + _isPreparingForSuspend = true; + + // Before suspend, save any final log file. + StorageFile finalLogFile = co_await CloseSessionSaveFinalLogFile(); + + // Cleanup the session. + _session.Close(); + _session = nullptr; + + // Save values used when the app is resumed or started later. + // Logging is enabled. + _settings.Insert(LOGGING_ENABLED_SETTING_KEY_NAME, box_value(true)); + + // If there's a final log file... + if (finalLogFile != nullptr) + { + // Save the log file name saved at suspend so the sample UI can be + // updated on resume with that information. + _settings.Insert(LOGFILEGEN_BEFORE_SUSPEND_SETTING_KEY_NAME, box_value(finalLogFile.Path())); + } + else + { + _settings.TryRemove(LOGFILEGEN_BEFORE_SUSPEND_SETTING_KEY_NAME); + } + + _isPreparingForSuspend = false; + } + else + { + // Save values used when the app is resumed or started later. + // Logging is not enabled and no log file was saved. + _settings.Insert(LOGGING_ENABLED_SETTING_KEY_NAME, box_value(false)); + _settings.TryRemove(LOGFILEGEN_BEFORE_SUSPEND_SETTING_KEY_NAME); + } +} + +// This is called when the app is either resuming or starting. +// It will enable logging if the app has never been started before +// or if logging had been enabled the last time the app was running. +void +FileLoggingSessionScenario::ResumeLoggingIfApplicable() +{ + bool loggingEnabled = unbox_value_or(_settings.TryLookup(LOGGING_ENABLED_SETTING_KEY_NAME), true); + if (loggingEnabled) + { + StartLogging(); + } + + // When the sample suspends, it retains state as to whether or not it had + // generated a new log file at the last suspension. This allows any + // UI to be updated on resume to reflect that fact. + hstring pathOfLogFileGeneratedBeforeSuspend = unbox_value_or(_settings.TryLookup(LOGFILEGEN_BEFORE_SUSPEND_SETTING_KEY_NAME), L""); + if (!pathOfLogFileGeneratedBeforeSuspend.empty()) + { + _statusChanged(make(LoggingScenarioEventType::LogFileGeneratedAtSuspend, pathOfLogFileGeneratedBeforeSuspend)); + _settings.TryRemove(LOGFILEGEN_BEFORE_SUSPEND_SETTING_KEY_NAME); + } +} + +IAsyncOperation +FileLoggingSessionScenario::CloseSessionSaveFinalLogFile() +{ + // Tell the session to save its final log file. + StorageFile finalFileBeforeSuspend = co_await _session.CloseAndSaveToFileAsync(); + + if (finalFileBeforeSuspend != nullptr) + { + // A final log file was created. + // Move it to the sample-defined log folder. + // First, get create/open our sample-defined log folder. + StorageFolder ourSampleAppLogFolder = + co_await ApplicationData::Current().LocalFolder().CreateFolderAsync( + OUR_SAMPLE_APP_LOG_FILE_FOLDER_NAME, + CreationCollisionOption::OpenIfExists); + + std::wstring logFileName; + logFileName += L"Log-"; + logFileName += GetTimeStamp(); + logFileName += L".etl"; + co_await finalFileBeforeSuspend.MoveAsync(ourSampleAppLogFolder, logFileName, NameCollisionOption::FailIfExists); + + std::wstring fullPath; + fullPath += ourSampleAppLogFolder.Path(); + fullPath += L'\\'; + fullPath += logFileName; + + StorageFile movedFile = co_await StorageFile::GetFileFromPathAsync(hstring(fullPath)); + return movedFile; + } + + return nullptr; +} + +void +FileLoggingSessionScenario::OnChannelLoggingEnabled( + ILoggingChannel const& sender, + IInspectable const&) +{ + // This method is called when the channel is informing us of channel-related state changes. + // Save new channel state. These values can be used for advanced logging scenarios where, + // for example, it's desired to skip blocks of logging code if the channel is not being + // consumed by any sessions. + _isChannelEnabled = sender.Enabled(); + _channelLoggingLevel = sender.Level(); +} + +// When the log file gets large, the system closes it and starts a new one. +// The LogFileGenerated event is raised to give the app a chance to move +// the recently-generated log file to a safe place. When the handler returns, +// the FileLoggingSession may reuse the file name for a new log file. +// This function moves the log file to an app-defined location. +fire_and_forget +FileLoggingSessionScenario::OnLogFileGenerated( + IFileLoggingSession const& sender, + LogFileGeneratedEventArgs const& args) +{ + ++_logFileGeneratedCount; + + StorageFile generatedLogFile = args.File(); + + // Create the app-defined folder under the app's local folder. + // An app defines where/how it wants to store log files. + // This sample uses OUR_SAMPLE_APP_LOG_FILE_FOLDER_NAME under + // the app's local folder. + StorageFolder ourSampleAppLogFolder = + co_await ApplicationData::Current().LocalFolder().CreateFolderAsync( + OUR_SAMPLE_APP_LOG_FILE_FOLDER_NAME, + CreationCollisionOption::OpenIfExists); + + std::wstring logFileName; + logFileName += L"Log-"; + logFileName += GetTimeStamp(); + logFileName += L".etl"; + + // Move the log file to our sample app's log folder. + co_await generatedLogFile.MoveAsync(ourSampleAppLogFolder, logFileName, NameCollisionOption::FailIfExists); + + // Update the UI to show the new log file. + if (_isPreparingForSuspend == false) + { + hstring fullPath = ourSampleAppLogFolder.Path() + L"\\" + logFileName; + _statusChanged(make(LoggingScenarioEventType::LogFileGenerated, fullPath)); + } +} + +fire_and_forget +FileLoggingSessionScenario::OnAppSuspending( + IInspectable const&, + Windows::ApplicationModel::SuspendingEventArgs const& e) +{ + // Get a deferral before performing any async operations + // to avoid suspension prior to LoggingScenario completing + // PrepareToSuspendAsync(). + auto deferral = e.SuspendingOperation().GetDeferral(); + co_await PrepareToSuspendAsync(); + deferral.Complete(); +} + +void FileLoggingSessionScenario::OnAppResuming( + IInspectable const&, + IInspectable const&) +{ + // If logging was active at the last suspend, + // ResumeLoggingIfApplicable will re-activate + // logging. + ResumeLoggingIfApplicable(); +} + +FileLoggingSessionScenario::BusySetter::~BusySetter() +{ + if (0 == --_scenario._isBusy) + { + _scenario._statusChanged(make(LoggingScenarioEventType::BusyStatusChanged)); + } +} + +FileLoggingSessionScenario::BusySetter::BusySetter(FileLoggingSessionScenario& scenario) + : _scenario(scenario) +{ + if (1 == ++_scenario._isBusy) + { + _scenario._statusChanged(make(LoggingScenarioEventType::BusyStatusChanged)); + } +} diff --git a/Samples/Logging/cppwinrt/FileLoggingSessionScenario.h b/Samples/Logging/cppwinrt/FileLoggingSessionScenario.h new file mode 100644 index 0000000000..57931f8b52 --- /dev/null +++ b/Samples/Logging/cppwinrt/FileLoggingSessionScenario.h @@ -0,0 +1,104 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once +#include "LoggingScenarioEventArgs.h" + +namespace winrt::SDKTemplate +{ + class FileLoggingSessionScenario + { + // This is a singleton, only created/destroyed by the Instance() method. + ~FileLoggingSessionScenario(); + FileLoggingSessionScenario(); + + public: + + static FileLoggingSessionScenario& + Instance(); + + event_token + StatusChanged(StatusChangedHandler const& handler); + + void + StatusChanged(event_token const& token) noexcept; + + bool + IsBusy() const noexcept; + + int + LogFileGeneratedCount() const noexcept; + + bool + LoggingEnabled() const noexcept; + + Windows::Foundation::IAsyncOperation + ToggleLoggingEnabledDisabledAsync(); + + Windows::Foundation::IAsyncAction + DoScenarioAsync(); + + private: + + void + StartLogging(); + + Windows::Foundation::IAsyncAction + PrepareToSuspendAsync(); + + void + ResumeLoggingIfApplicable(); + + Windows::Foundation::IAsyncOperation + CloseSessionSaveFinalLogFile(); + + void + OnChannelLoggingEnabled( + Windows::Foundation::Diagnostics::ILoggingChannel const& sender, + Windows::Foundation::IInspectable const& args); + + fire_and_forget + OnLogFileGenerated( + Windows::Foundation::Diagnostics::IFileLoggingSession const& sender, + Windows::Foundation::Diagnostics::LogFileGeneratedEventArgs const& args); + + fire_and_forget + OnAppSuspending(Windows::Foundation::IInspectable const& sender, + Windows::ApplicationModel::SuspendingEventArgs const& e); + + void + OnAppResuming(Windows::Foundation::IInspectable const& sender, + Windows::Foundation::IInspectable const& args); + + private: + + struct BusySetter + { + ~BusySetter(); + explicit BusySetter(FileLoggingSessionScenario& scenario); + FileLoggingSessionScenario& _scenario; + }; + + Windows::Foundation::Diagnostics::FileLoggingSession _session{ nullptr }; + Windows::Foundation::Diagnostics::LoggingChannel _channel{ nullptr }; + + std::atomic _logFileGeneratedCount; + std::atomic _isBusy; + bool _isPreparingForSuspend = false; + + bool _isChannelEnabled = false; + Windows::Foundation::Diagnostics::LoggingLevel _channelLoggingLevel{}; + + Windows::Foundation::Collections::IPropertySet _settings{ Windows::Storage::ApplicationData::Current().LocalSettings().Values() }; + + event _statusChanged; + }; +} diff --git a/Samples/Logging/cppwinrt/Logging.sln b/Samples/Logging/cppwinrt/Logging.sln new file mode 100644 index 0000000000..f958ec9f1f --- /dev/null +++ b/Samples/Logging/cppwinrt/Logging.sln @@ -0,0 +1,43 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30320.27 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Logging", "Logging.vcxproj", "{39D38C25-25B2-4344-A60B-0B45EB32A2D5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM = Release|ARM + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {39D38C25-25B2-4344-A60B-0B45EB32A2D5}.Debug|ARM.ActiveCfg = Debug|ARM + {39D38C25-25B2-4344-A60B-0B45EB32A2D5}.Debug|ARM.Build.0 = Debug|ARM + {39D38C25-25B2-4344-A60B-0B45EB32A2D5}.Debug|ARM.Deploy.0 = Debug|ARM + {39D38C25-25B2-4344-A60B-0B45EB32A2D5}.Debug|x64.ActiveCfg = Debug|x64 + {39D38C25-25B2-4344-A60B-0B45EB32A2D5}.Debug|x64.Build.0 = Debug|x64 + {39D38C25-25B2-4344-A60B-0B45EB32A2D5}.Debug|x64.Deploy.0 = Debug|x64 + {39D38C25-25B2-4344-A60B-0B45EB32A2D5}.Debug|x86.ActiveCfg = Debug|Win32 + {39D38C25-25B2-4344-A60B-0B45EB32A2D5}.Debug|x86.Build.0 = Debug|Win32 + {39D38C25-25B2-4344-A60B-0B45EB32A2D5}.Debug|x86.Deploy.0 = Debug|Win32 + {39D38C25-25B2-4344-A60B-0B45EB32A2D5}.Release|ARM.ActiveCfg = Release|ARM + {39D38C25-25B2-4344-A60B-0B45EB32A2D5}.Release|ARM.Build.0 = Release|ARM + {39D38C25-25B2-4344-A60B-0B45EB32A2D5}.Release|ARM.Deploy.0 = Release|ARM + {39D38C25-25B2-4344-A60B-0B45EB32A2D5}.Release|x64.ActiveCfg = Release|x64 + {39D38C25-25B2-4344-A60B-0B45EB32A2D5}.Release|x64.Build.0 = Release|x64 + {39D38C25-25B2-4344-A60B-0B45EB32A2D5}.Release|x64.Deploy.0 = Release|x64 + {39D38C25-25B2-4344-A60B-0B45EB32A2D5}.Release|x86.ActiveCfg = Release|Win32 + {39D38C25-25B2-4344-A60B-0B45EB32A2D5}.Release|x86.Build.0 = Release|Win32 + {39D38C25-25B2-4344-A60B-0B45EB32A2D5}.Release|x86.Deploy.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {8B3EAE62-ABE2-4450-BDFD-0A35E56B141B} + EndGlobalSection +EndGlobal diff --git a/Samples/Logging/cppwinrt/Logging.vcxproj b/Samples/Logging/cppwinrt/Logging.vcxproj new file mode 100644 index 0000000000..80e8ecc49c --- /dev/null +++ b/Samples/Logging/cppwinrt/Logging.vcxproj @@ -0,0 +1,215 @@ + + + + + $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), LICENSE))\SharedContent + + + true + {39D38C25-25B2-4344-A60B-0B45EB32A2D5} + Logging + SDKTemplate + en-US + 15.0 + true + Windows Store + 10.0 + 10.0.18362.0 + $(WindowsTargetPlatformVersion) + + + + + Debug + ARM + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM + + + Release + Win32 + + + Release + x64 + + + + Application + Unicode + + + true + true + + + false + true + false + + + + + + + + $(VC_IncludePath);$(UniversalCRT_IncludePath);$(WindowsSDK_IncludePath);$(SharedContentDir)\cppwinrt + true + + + + Use + pch.h + $(IntDir)pch.pch + Level4 + %(AdditionalOptions) /bigobj + 4453;28204 + + + + + _DEBUG;%(PreprocessorDefinitions) + + + + + NDEBUG;%(PreprocessorDefinitions) + + + + + $(SharedContentDir)\xaml\App.xaml + + + $(SharedContentDir)\xaml\MainPage.xaml + + + + + + + + + ..\shared\Scenario1.xaml + Code + + + + ..\shared\Scenario2.xaml + Code + + + ..\shared\Scenario3.xaml + Code + + + + + Designer + + + Designer + + + Designer + + + Styles\Styles.xaml + + + Designer + + + Designer + + + + + $(SharedContentDir)\xaml\App.xaml + + + $(SharedContentDir)\xaml\MainPage.xaml + + + + + + SampleConfiguration.h + + + ..\shared\Scenario1.xaml + Code + + + Create + pch.h + + + Project.idl + + + ..\shared\Scenario2.xaml + Code + + + ..\shared\Scenario3.xaml + Code + + + + + $(SharedContentDir)\xaml\MainPage.xaml + + + + + + Designer + + + + + + Assets\microsoft-sdk.png + + + Assets\smallTile-sdk.png + + + Assets\splash-sdk.png + + + Assets\squareTile-sdk.png + + + Assets\storeLogo-sdk.png + + + Assets\tile-sdk.png + + + Assets\windows-sdk.png + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/Samples/Logging/cppwinrt/Logging.vcxproj.filters b/Samples/Logging/cppwinrt/Logging.vcxproj.filters new file mode 100644 index 0000000000..5639c480fa --- /dev/null +++ b/Samples/Logging/cppwinrt/Logging.vcxproj.filters @@ -0,0 +1,68 @@ + + + + + 4416d50a-7676-4d0a-9b2c-91ff70c6047f + bmp;fbx;gif;jpg;jpeg;tga;tiff;tif;png + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + + + + + + + \ No newline at end of file diff --git a/Samples/Logging/cppwinrt/LoggingChannelScenario.cpp b/Samples/Logging/cppwinrt/LoggingChannelScenario.cpp new file mode 100644 index 0000000000..45060feb41 --- /dev/null +++ b/Samples/Logging/cppwinrt/LoggingChannelScenario.cpp @@ -0,0 +1,316 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "pch.h" +#include "LoggingChannelScenario.h" + +using namespace winrt; +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::Foundation::Diagnostics; +using namespace winrt::SDKTemplate; + +/// +/// Construct a LoggingChannel with the Windows 8.1 constructor, then +/// use the LoggingChannel for the scenario. +/// See this sample's README for further discussion. +/// +void +LoggingChannelScenario::LogWithWin81Constructor() +{ + // The 1-parameter constructor creates a channel with Windows 8.1 semantics. + LoggingChannel channel(L"SampleProvider"); + DemonstrateLogging(channel); +} + +/// +/// Construct a LoggingChannel with the Windows 10 constructor, then +/// use the LoggingChannel for the scenario. Also show the use cases +/// for the two Windows 10 constructors. +// See this sample's README for further discussion. +/// +void +LoggingChannelScenario::LogWithWin10Constructor() +{ + { + // The 2-parameter constructor creates a channel with Windows 10 semantics. + LoggingChannel channel( + L"SampleProvider", + nullptr); // null means use default options. + + // The Id is generated by hashing the string "SampleProvider". + // channel.Id == eff1e128-4903-5093-096a-bdc29b38456f + DemonstrateLogging(channel); + } + + /* + Demonstrate other (less-common) constructor scenarios: + */ + + { + // This creates a channel with Windows 10 semantics and declared + // membership in a provider group. + GUID g = { 0x2e0582f3,0xd1b6,0x516a,0x9d,0xe3,0x9f,0xd7,0x9e,0xf9,0x52,0xf8 }; + LoggingChannel channel( + L"SampleProvider", + Windows::Foundation::Diagnostics::LoggingChannelOptions(g)); // Join a provider group + // The Id is generated by hashing the string "SampleProvider". + // channel.Id == eff1e128-4903-5093-096a-bdc29b38456f + } + + { + // This creates a channel with Windows 10 semantics and a specific + // provider Id. + GUID g = { 0x2e0582f3,0xd1b6,0x516a,0x9d,0xe3,0x9f,0xd7,0x9e,0xf9,0x52,0xf8 }; + LoggingChannel channel( + L"SampleProvider", + nullptr, + g); + // channel.Id == 2e0582f3-d1b6-516a-9de3-9fd79ef952f8 + } +} + +/// +/// This method demonstrates the LoggingChannel and LoggingActivity APIs. +/// +/// +/// The channel to use for the demonstration. This channel may have been +/// constructed using a Windows 8.1 constructor or a Windows 10 constructor. +/// The same APIs are supported in both cases, but the ETL events will be +/// formatted a bit differently depending on how the channel was constructed. +/// +void +LoggingChannelScenario::DemonstrateLogging( + LoggingChannel const& channel) +{ + // Whenever any ETW session changes the way it is listening to this + // channel, the LoggingEnable event is fired. For example, this might + // be called when a session begins listening, changes the level at + // which it is listening, or stops listening. + event_token loggingEnabledToken = channel.LoggingEnabled({ this, &LoggingChannelScenario::OnLoggingEnabled }); + + // Log simple string events + channel.LogMessage(L"Simple message"); // default level is Verbose + channel.LogMessage(L"Simple error", Windows::Foundation::Diagnostics::LoggingLevel::Error); + + // Log simple string + integer events. + channel.LogValuePair(L"Simple message", 123); // default level is Verbose + channel.LogValuePair(L"Simple error", 456, LoggingLevel::Error); + + // The channel.Name property returns the name that was used when the + // channel was constructed. When running in Windows 10 mode, the name + // is already set as the provider name, so no LoggingChannelName is + // automatically added to the event. + channel.LogMessage(channel.Name()); + + // The channel.Id property is new to Windows 10. + channel.LogMessage(to_hstring(channel.Id())); + + // If you want to avoid the overhead of collecting data when nobody is + // listening to your channel, check the Enabled property before logging. + if (channel.Enabled()) + { + channel.LogMessage(CollectExpensiveData()); + } + + // The IsEnabled() method is exactly the same as the Enabled property, + // except that it is a new Windows 10 API. + if (channel.IsEnabled()) + { + channel.LogMessage(CollectExpensiveData()); + } + + // If you want to only collect data if somebody is listening at a specific + // level, you need to check both Enabled and Level. Note that the value of + // the Level is unspecified when Enabled is false. + if (channel.Enabled() && channel.Level() <= LoggingLevel::Warning) + { + channel.LogMessage(CollectExpensiveData(), LoggingLevel::Warning); + } + + // The IsEnabled(LoggingLevel) method is a bit nicer than checking both + // Enabled and Level, but it is only available on Windows 10 or later. + if (channel.IsEnabled(LoggingLevel::Warning)) + { + channel.LogMessage(CollectExpensiveData(), LoggingLevel::Warning); + } + + // You can also use IsEnabled to check for keywords. + if (channel.IsEnabled(LoggingLevel::Information, 0x10)) + { + channel.LogMessage(CollectExpensiveData(), LoggingLevel::Information); + } + + // Use LoggingFields with the LogEvent method to write complex events. + Windows::Foundation::Diagnostics::LoggingFields fields; + fields.AddDouble(L"pi", 3.14159); + channel.LogEvent( + L"ComplexEvent", + fields, + LoggingLevel::Verbose, + LoggingOptions(0x10)); // Keywords = 0x10 + + // You can add any number of name-value pairs to a fields object, though + // you may encounter ETW limitations if you add too many. For example, + // ETW is limited to a maximum event size of 64KB, and the current + // TraceLogging decoder can handle no more than 128 fields. + + // Performance optimization: You can reuse a LoggingFields object to + // avoid unnecessary allocations. Don't forget to call Clear() + // between uses, and don't try to share a LoggingFields object between + // threads. + DateTime now = clock::now(); + fields.Clear(); + fields.AddInt64(L"Date", clock::to_file_time(now).value, LoggingFieldFormat::FileTime); + channel.LogEvent(L"Now", fields); + + fields.Clear(); + + // You can add a formatting hint to affect the way a value is decoded. + // Not all combinations are useful, and the hint may be ignored. + // For example, you can encode an MBCS string by writing a byte array + // with a String hint. + uint8_t abc123[] = { 65, 66, 67, 49, 50, 51 }; // "ABC123" + fields.AddUInt8Array( + L"AnsiString", + abc123, + LoggingFieldFormat::String); + + // You can add "tag" bits to a field. These are user-defined bits that + // can be used to communicate with an event processing tool. For example, + // you might define a tag bit to indicate that a field contains private + // data that should not be displayed on-screen. + fields.AddString(L"Password", L"12345", LoggingFieldFormat::Default, 0x10); + + // You can add a "structure" to an event. A structure is a name for a + // group of fields. Structures can nest. Call BeginStruct to add a level + // of nesting, and call EndStruct after the last field of the structure. + fields.BeginStruct(L"Nested"); + fields.AddInt16(L"Nested-1", 1); + fields.AddInt16(L"Nested-2", 2); + fields.BeginStruct(L"Nested-Nested"); + fields.AddInt16(L"Nested-Nested-3", 3); + fields.EndStruct(); + fields.AddInt16(L"Nested-4", 4); + fields.EndStruct(); + + // Advanced scenarios: you can use a LoggingOptions object to control + // detailed event settings such as keywords, opcodes, and activity Ids. + // These have their normal ETW semantics. You can also set event tags, + // which are bit values that can be used to communicate with the event + // processor. + LoggingOptions options; + options.Keywords(0x123); + options.Tags(0x10); + channel.LogEvent( + L"VeryComplexEvent", + fields, + LoggingLevel::Information, + options); + + // Windows 10 introduces the ILoggingTarget interface. LoggingChannel + // implements this interface. This interface allows components to accept + // a logger as an parameter. + DoSomething(channel); + + /* + If a LoggingActivity is created using a LoggingActivity constructor, + it will use Windows 8.1 semantics: + + - If an activity is destroyed (garbage-collected) without being closed + and the associated LoggingChannel is still open, the activity will + write a default Stop event. + - The default Stop event (written by the destructor or by the Close() + method) is encoded as a "simple" event. + + The 8.1 semantics are deprecated because the automatic generation of + a Stop event at garbage-collection can be misleading. The Stop event + is intended to mark the a precise point at which an activity is + completed, while the garbage-collection of an abandoned activity is + inherently imprecise and unpredictable. + + If a LoggingActivity is created using a StartActivity method, it will + use Windows 10 semantics: + + - If an activity is destroyed (garbage-collected) without being closed, + there will be no Stop event for the activity. + - The default Stop event (written by the Close() method) is encoded as + a TraceLogging event with name "ActivityClosed". + */ + + { + // This activity is created with Windows 8.1 semantics. + LoggingActivity a1(L"Activity1", channel); + + // The activity Start event is written by the LoggingActivity constructor. + // You would do your activity's work here. + // The activity Stop event is written when the activity is closed (disposed). + + // The Windows 10 LoggingActivity adds new methods for writing events + // that are marked as associated with the activity. + a1.LogEvent(L"Activity event"); + + // LoggingActivity also implements the ILoggingTarget interface, so you can + // use either a channel or an activity as a logging target. + DoSomething(a1); + + { + // The Windows 10 LoggingActivity adds new methods for creating nested activities. + // Note that nested activities are always created with Windows 10 semantics, + // even when nested under an activity that is using Windows 8.1 semantics. + LoggingActivity a2 = a1.StartActivity(L"Activity2"); + + // Nested task occurs here. + + // The Windows 10 LoggingActivity allows you to customize the Stop event. + a2.StopActivity(L"Activity 2 stop"); + } + + // Because a1 is using Windows 8.1 semantics, it will write a Stop event when + // it goes out of scope. + } + + { + // The Windows 10 StartActivity method creates a new activity, optionally with + // specified fields and characteristics. + // This activity is created with Windows 10 semantics. + LoggingActivity a3 = channel.StartActivity(L"Activity3"); + + // Because a3 is using Windows 10 semantics, if we did not call StopActivity(), + // there would be no Stop event (not even when the activity is garbage + // collected). To get a Stop event, be sure to stop, close, or dispose the + // activity. + a3.StopActivity(L"ActivityClosed"); + } + + // Unregister from the event. + channel.LoggingEnabled(loggingEnabledToken); +} + +hstring +LoggingChannelScenario::CollectExpensiveData() +{ + return L"ExpensiveData"; +} + +void +LoggingChannelScenario::DoSomething( + ILoggingTarget const& logger) +{ + logger.LogEvent(L"Did something"); +} + +void +LoggingChannelScenario::OnLoggingEnabled( + ILoggingChannel const&, + IInspectable const&) +{ + // Here, you could note a change in the level or keywords. +} diff --git a/Samples/Logging/cppwinrt/LoggingChannelScenario.h b/Samples/Logging/cppwinrt/LoggingChannelScenario.h new file mode 100644 index 0000000000..5c8e1ee710 --- /dev/null +++ b/Samples/Logging/cppwinrt/LoggingChannelScenario.h @@ -0,0 +1,44 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +namespace winrt::SDKTemplate +{ + class LoggingChannelScenario + { + public: + + void + LogWithWin81Constructor(); + + void + LogWithWin10Constructor(); + + private: + + void + DemonstrateLogging( + Windows::Foundation::Diagnostics::LoggingChannel const& channel); + + hstring + CollectExpensiveData(); + + void + DoSomething( + Windows::Foundation::Diagnostics::ILoggingTarget const& logger); + + void + OnLoggingEnabled( + Windows::Foundation::Diagnostics::ILoggingChannel const& sender, + Windows::Foundation::IInspectable const& e); + }; +} diff --git a/Samples/Logging/cppwinrt/LoggingScenarioEventArgs.h b/Samples/Logging/cppwinrt/LoggingScenarioEventArgs.h new file mode 100644 index 0000000000..8ef10f400e --- /dev/null +++ b/Samples/Logging/cppwinrt/LoggingScenarioEventArgs.h @@ -0,0 +1,40 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once +#include "LoggingScenarioEventArgs.g.h" + +namespace winrt::SDKTemplate::implementation +{ + struct LoggingScenarioEventArgs : LoggingScenarioEventArgsT + { + // e.g. for BusyStatusChanged + LoggingScenarioEventArgs(LoggingScenarioEventType type) + : _type(type) {} + + // e.g. for LogFileGenerated + LoggingScenarioEventArgs(LoggingScenarioEventType type, hstring const& logFileFullPath) + : _type(type), _logFileFullPath(logFileFullPath) {} + + // e.g. for LoggingEnabledDisabled + LoggingScenarioEventArgs(LoggingScenarioEventType type, bool enabled) + : _type(type), _enabled(enabled) {} + + LoggingScenarioEventType Type() const noexcept { return _type; } + bool Enabled() const noexcept { return _enabled; } + hstring LogFileFullPath() const noexcept { return _logFileFullPath; } + + private: + LoggingScenarioEventType _type; + bool _enabled = false; + hstring _logFileFullPath; + }; +} diff --git a/Samples/Logging/cppwinrt/LoggingScenarioHelpers.h b/Samples/Logging/cppwinrt/LoggingScenarioHelpers.h new file mode 100644 index 0000000000..6d5dbaf7dc --- /dev/null +++ b/Samples/Logging/cppwinrt/LoggingScenarioHelpers.h @@ -0,0 +1,82 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +namespace winrt::SDKTemplate +{ + // Create a time stamp to append to log file names so + // each file has a unique name. The format is: + // YYMMDD-hhmmssMMM + // where + // YY == year + // MM == month + // DD == day + // hh == hours + // mm == minutes + // ss == seconds + // MMM == milliseconds + inline std::wstring + GetTimeStamp() + { + std::wstring result; + SYSTEMTIME timeNow; + GetLocalTime(&timeNow); + result.resize(16); + swprintf_s( + &result[0], + result.size() + 1, + L"%02u%02u%02u-%02u%02u%02u%03u", + timeNow.wYear - 2000, + timeNow.wMonth, + timeNow.wDay, + timeNow.wHour, + timeNow.wMinute, + timeNow.wSecond, + timeNow.wMilliseconds); + return result; + } + + inline Windows::UI::Xaml::Controls::ScrollViewer + FindScrollViewer(Windows::UI::Xaml::DependencyObject const& control) + { + if (control == nullptr) + { + return nullptr; + } + + int childCount = Windows::UI::Xaml::Media::VisualTreeHelper::GetChildrenCount(control); + if (childCount <= 0) + { + return nullptr; + } + + for (int childIndex = 0; childIndex < childCount; childIndex++) + { + Windows::UI::Xaml::DependencyObject childControl = + Windows::UI::Xaml::Media::VisualTreeHelper::GetChild(control, childIndex); + Windows::UI::Xaml::Controls::ScrollViewer viewer = + childControl.try_as(); + if (viewer != nullptr) + { + return viewer; + } + + viewer = FindScrollViewer(childControl); + if (viewer != nullptr) + { + return viewer; + } + } + + return nullptr; + } +} diff --git a/Samples/Logging/cppwinrt/LoggingSessionScenario.cpp b/Samples/Logging/cppwinrt/LoggingSessionScenario.cpp new file mode 100644 index 0000000000..d16e78b72c --- /dev/null +++ b/Samples/Logging/cppwinrt/LoggingSessionScenario.cpp @@ -0,0 +1,338 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "pch.h" +#include "LoggingSessionScenario.h" +#include "LoggingScenarioHelpers.h" + +#define SCENARIO_PREFIX L"LoggingSessionScenario_" +#define DEFAULT_SESSION_NAME SCENARIO_PREFIX L"Session" +#define DEFAULT_CHANNEL_NAME SCENARIO_PREFIX L"Channel" +#define OUR_SAMPLE_APP_LOG_FILE_FOLDER_NAME SCENARIO_PREFIX L"LogFiles" +#define LOGGING_ENABLED_SETTING_KEY_NAME SCENARIO_PREFIX L"LoggingEnabled" + +using namespace winrt; +using namespace winrt::Windows::ApplicationModel; +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::Foundation::Diagnostics; +using namespace winrt::Windows::Storage; +using namespace winrt::SDKTemplate; + +LoggingSessionScenario::~LoggingSessionScenario() +{ +} + +LoggingSessionScenario::LoggingSessionScenario() +{ + // Create the logging channel. + // When an app logs messages to a channel, the messges will go + // to any sessions which are consuming messages from the channel. + _channel = LoggingChannel(DEFAULT_CHANNEL_NAME); + + // This sample listens to the channel's events so it can track + // information about the channel's enablement status. + _channel.LoggingEnabled({ this, &LoggingSessionScenario::OnChannelLoggingEnabled }); + + // Attach event handlers for Suspend/Resume. + Windows::UI::Xaml::Application app = Windows::UI::Xaml::Application::Current(); + app.Suspending({ this, &LoggingSessionScenario::OnAppSuspending }); + app.Resuming({ this, &LoggingSessionScenario::OnAppResuming }); + + // If the app is being launched (not resumed), the following call will + // activate logging if it had been active at the time of the last suspend. + ResumeLoggingIfApplicable(); +} + +// static +LoggingSessionScenario& +LoggingSessionScenario::Instance() +{ + static LoggingSessionScenario instance; + return instance; +} + +event_token +LoggingSessionScenario::StatusChanged(StatusChangedHandler const& handler) +{ + return _statusChanged.add(handler); +} + +void +LoggingSessionScenario::StatusChanged(event_token const& token) noexcept +{ + _statusChanged.remove(token); +} + +bool +LoggingSessionScenario::IsBusy() const noexcept +{ + return _isBusy != 0; +} + +int +LoggingSessionScenario::LogFileGeneratedCount() const noexcept +{ + return _logFileGeneratedCount; +} + +bool +LoggingSessionScenario::LoggingEnabled() const noexcept +{ + return _session != nullptr; +} + +bool +LoggingSessionScenario::ToggleLoggingEnabledDisabled() +{ + BusySetter busy(*this); + + if (_session != nullptr) + { + _session.Close(); + _session = nullptr; + _settings.Insert(LOGGING_ENABLED_SETTING_KEY_NAME, box_value(false)); + _statusChanged(make(LoggingScenarioEventType::LoggingEnabledDisabled, false)); + return false; + } + else + { + StartLogging(); + _settings.Insert(LOGGING_ENABLED_SETTING_KEY_NAME, box_value(true)); + _statusChanged(make(LoggingScenarioEventType::LoggingEnabledDisabled, true)); + return true; + } +} + +IAsyncAction +LoggingSessionScenario::DoScenarioAsync() +{ + BusySetter busy(*this); + + std::wstring messageToLog; + + int messageIndex = 0; // Increments for each logging call. + int lastDelayIndex = 0; // The messageIndex value at the last delay. + int lastAppErrorIndex = 0; // The messageIndex value at the last simulated error. + int startFileCount = LogFileGeneratedCount(); + + const int NUMBER_OF_LOG_FILES_TO_GENERATE = 3; + + while (LogFileGeneratedCount() - startFileCount < NUMBER_OF_LOG_FILES_TO_GENERATE) + { + bool exceptionOccurred = false; + try + { + // Since the channel is added to the session at level Warning, + // the following is logged because it is logged at level LoggingLevel.Critical. + messageToLog = L"Message="; + messageToLog += std::to_wstring(++messageIndex); + messageToLog += + L": Lorem ipsum dolor sit amet, consectetur adipiscing elit.In ligula nisi, vehicula nec eleifend vel, rutrum non dolor.Vestibulum ante ipsum " + L"primis in faucibus orci luctus et ultrices posuere cubilia Curae; Curabitur elementum scelerisque accumsan. In hac habitasse platea dictumst."; + _channel.LogMessage(messageToLog, LoggingLevel::Critical); + + // Since the channel is added to the session at level Warning, + // the following is *not* logged because it is logged at LoggingLevel.Information. + messageToLog = L"Message="; + messageToLog += std::to_wstring(++messageIndex); + messageToLog += + L": Lorem ipsum dolor sit amet, consectetur adipiscing elit.In ligula nisi, vehicula nec eleifend vel, rutrum non dolor.Vestibulum ante ipsum " + L"primis in faucibus orci luctus et ultrices posuere cubilia Curae; Curabitur elementum scelerisque accumsan. In hac habitasse platea dictumst."; + _channel.LogMessage(messageToLog, LoggingLevel::Information); + + int value = 1000000; // one million, 7 digits, 4-bytes as an int, 14 bytes as a wide character string. + messageToLog = L"Value #"; + messageToLog += std::to_wstring(++messageIndex); + messageToLog += L':'; + messageToLog += std::to_wstring(value); + _channel.LogMessage(messageToLog, LoggingLevel::Critical); // 'value' is logged as 14 byte wide character string. + ++messageIndex; + _channel.LogValuePair(L"Value", value, LoggingLevel::Critical); // 'value' is logged as a 4-byte integer. + + // + // Every once in a while, simulate an application error + // which causes the app to save the current snapshot + // of logging events in memory to a disk ETL file. + // + + if (messageIndex - lastAppErrorIndex >= 25000) + { + lastAppErrorIndex = messageIndex; + + // Before simulating an application error, demonstrate LoggingActivity. + // A LoggingActivity outputs a pair of begin and end messages to the channel. + // The first message is logged when the LoggingActivity is created. When + // exiting the scope of the LoggingActivity, the second message will be logged. + { + LoggingActivity activity(L"Add two numbers.", _channel, + LoggingLevel::Critical); + int oneNumber = 100; + int anotherNumber = 200; + int total = oneNumber + anotherNumber; + messageToLog = L"Message="; + messageToLog += std::to_wstring(++messageIndex); + messageToLog += L": The result of adding two numbers: "; + messageToLog += std::to_wstring(total); + _channel.LogMessage(messageToLog, LoggingLevel::Critical); + } // The LoggingActivity instance will log the end message at this scope exit. + + // Simulate an application error. + throw std::exception("Some bad app error occurred."); + } + + // + // Pause every once in a while to simulate application activity outside of logging. + // + + if (messageIndex - lastDelayIndex >= 100) + { + lastDelayIndex = messageIndex; + co_await resume_after(std::chrono::milliseconds(5)); + } + } + catch (std::exception const& e) + { + // Save in-memory logging buffer to a log file on error. + + hstring what; + if (e.what() != nullptr) + { + what = to_hstring(e.what()); + } + else + { + what = L"Unknown."; + } + + // The session added the channel at level Warning. Log the message at + // level Error which is above (more critical than) Warning, which + // means it will actually get logged. + _channel.LogMessage(L"Exception occurred: " + what, LoggingLevel::Error); + + // Since co_await is not supported within catch blocks, record the exception. + // After the catch block, we'll notice that an exception occurred and save the log. + exceptionOccurred = true; + } + + if (exceptionOccurred) + { + // An exception occurred, so save the in-memory events to disk for analysis. + + // Create the app-defined folder under the app's local folder. + // An app defines where/how it wants to store log files. + // This sample uses OUR_SAMPLE_APP_LOG_FILE_FOLDER_NAME under + // the app's local folder. + StorageFolder ourSampleAppLogFolder = + co_await ApplicationData::Current().LocalFolder().CreateFolderAsync( + OUR_SAMPLE_APP_LOG_FILE_FOLDER_NAME, + CreationCollisionOption::OpenIfExists); + + // Create a new log file name. + std::wstring newLogFileName; + newLogFileName += L"Log-"; + newLogFileName += GetTimeStamp(); + newLogFileName += L".etl"; + + StorageFile logFile = co_await _session.SaveToFileAsync(ourSampleAppLogFolder, newLogFileName); + ++_logFileGeneratedCount; + _statusChanged(make(LoggingScenarioEventType::LogFileGenerated, logFile.Path())); + } + } +} + +void +LoggingSessionScenario::StartLogging() +{ + // If no session exists, create one. + // NOTE: There are use cases where an application + // may want to create only a channel for sessions outside + // of the application itself. See MSDN for details. This + // sample is the common scenario of an app logging events + // which it wants to place in its own log file, so it creates + // a session and channel as a pair. The channel is created + // during construction of this LoggingSessionScenario class so + // it already exists by the time this function is called. + if (_session == nullptr) + { + _session = LoggingSession(DEFAULT_SESSION_NAME); + } + + // This sample adds the channel at level "warning" to + // demonstrate how messages logged at more verbose levels + // are ignored by the session. + _session.AddLoggingChannel(_channel, LoggingLevel::Warning); +} + +void +LoggingSessionScenario::PrepareToSuspend() +{ + _settings.Insert(LOGGING_ENABLED_SETTING_KEY_NAME, box_value(_session != nullptr)); +} + +void +LoggingSessionScenario::ResumeLoggingIfApplicable() +{ + bool loggingEnabled = unbox_value_or(_settings.TryLookup(LOGGING_ENABLED_SETTING_KEY_NAME), true); + if (loggingEnabled) + { + StartLogging(); + } +} + +void +LoggingSessionScenario::OnChannelLoggingEnabled( + ILoggingChannel const& sender, + IInspectable const&) +{ + // This method is called when the channel is informing us of channel-related state changes. + // Save new channel state. These values can be used for advanced logging scenarios where, + // for example, it's desired to skip blocks of logging code if the channel is not being + // consumed by any sessions. + _isChannelEnabled = sender.Enabled(); + _channelLoggingLevel = sender.Level(); +} + +void +LoggingSessionScenario::OnAppSuspending( + IInspectable const&, + SuspendingEventArgs const& e) +{ + auto deferral = e.SuspendingOperation().GetDeferral(); + PrepareToSuspend(); + deferral.Complete(); +} + +void +LoggingSessionScenario::OnAppResuming( + IInspectable const&, + IInspectable const&) +{ + // If logging was active at the last suspend, ResumeLoggingIfApplicable + // will re-activate logging. + ResumeLoggingIfApplicable(); +} + +LoggingSessionScenario::BusySetter::~BusySetter() +{ + if (0 == --_scenario._isBusy) + { + _scenario._statusChanged(make(LoggingScenarioEventType::BusyStatusChanged)); + } +} + +LoggingSessionScenario::BusySetter::BusySetter(LoggingSessionScenario& scenario) + : _scenario(scenario) +{ + if (1 == ++_scenario._isBusy) + { + _scenario._statusChanged(make(LoggingScenarioEventType::BusyStatusChanged)); + } +} diff --git a/Samples/Logging/cppwinrt/LoggingSessionScenario.h b/Samples/Logging/cppwinrt/LoggingSessionScenario.h new file mode 100644 index 0000000000..af618a9a90 --- /dev/null +++ b/Samples/Logging/cppwinrt/LoggingSessionScenario.h @@ -0,0 +1,97 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once +#include "LoggingScenarioEventArgs.h" + +namespace winrt::SDKTemplate +{ + class LoggingSessionScenario + { + // This is a singleton, only created/destroyed by the Instance() method. + ~LoggingSessionScenario(); + LoggingSessionScenario(); + + public: + + static LoggingSessionScenario& + Instance(); + + winrt::event_token + StatusChanged(StatusChangedHandler const& handler); + + void + StatusChanged(winrt::event_token const& token) noexcept; + + bool + IsBusy() const noexcept; + + int + LogFileGeneratedCount() const noexcept; + + bool + LoggingEnabled() const noexcept; + + bool + ToggleLoggingEnabledDisabled(); + + Windows::Foundation::IAsyncAction + DoScenarioAsync(); + + private: + + void + StartLogging(); + + void + PrepareToSuspend(); + + void + ResumeLoggingIfApplicable(); + + void + OnChannelLoggingEnabled( + Windows::Foundation::Diagnostics::ILoggingChannel const& sender, + Windows::Foundation::IInspectable const& args); + + void + OnAppSuspending( + Windows::Foundation::IInspectable const& sender, + Windows::ApplicationModel::SuspendingEventArgs const& e); + + void + OnAppResuming( + Windows::Foundation::IInspectable const& sender, + Windows::Foundation::IInspectable const& args); + + private: + + struct BusySetter + { + ~BusySetter(); + explicit BusySetter(LoggingSessionScenario& scenario); + LoggingSessionScenario& _scenario; + }; + + Windows::Foundation::Diagnostics::LoggingSession _session{ nullptr }; + Windows::Foundation::Diagnostics::LoggingChannel _channel{ nullptr }; + + std::atomic _logFileGeneratedCount; + std::atomic _isBusy; + + bool _isChannelEnabled = false; + Windows::Foundation::Diagnostics::LoggingLevel _channelLoggingLevel{}; + + Windows::Foundation::Collections::IPropertySet _settings{ Windows::Storage::ApplicationData::Current().LocalSettings().Values() }; + + event _statusChanged; + }; +} diff --git a/Samples/Logging/cppwinrt/Package.appxmanifest b/Samples/Logging/cppwinrt/Package.appxmanifest new file mode 100644 index 0000000000..e39a581e71 --- /dev/null +++ b/Samples/Logging/cppwinrt/Package.appxmanifest @@ -0,0 +1,42 @@ + + + + + + Logging C++/WinRT Sample + Microsoft Corporation + Assets\storelogo-sdk.png + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/Logging/cppwinrt/Project.idl b/Samples/Logging/cppwinrt/Project.idl new file mode 100644 index 0000000000..e7c6dedc69 --- /dev/null +++ b/Samples/Logging/cppwinrt/Project.idl @@ -0,0 +1,52 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +namespace SDKTemplate +{ + [default_interface] + runtimeclass Scenario1 : Windows.UI.Xaml.Controls.Page + { + Scenario1(); + } + + [default_interface] + runtimeclass Scenario2 : Windows.UI.Xaml.Controls.Page + { + Scenario2(); + } + + [default_interface] + runtimeclass Scenario3 : Windows.UI.Xaml.Controls.Page + { + Scenario3(); + } + + // LoggingScenario tells the UI what's happening by + // using the following enums. + enum LoggingScenarioEventType + { + BusyStatusChanged, + LogFileGenerated, + LogFileGeneratedAtDisable, + LogFileGeneratedAtSuspend, + LoggingEnabledDisabled, + }; + + runtimeclass LoggingScenarioEventArgs + { + LoggingScenarioEventType Type{ get; }; + Boolean Enabled{ get; }; + String LogFileFullPath{ get; }; + } + + delegate void StatusChangedHandler(LoggingScenarioEventArgs e); + +} diff --git a/Samples/Logging/cppwinrt/SampleConfiguration.cpp b/Samples/Logging/cppwinrt/SampleConfiguration.cpp new file mode 100644 index 0000000000..7edaa6395b --- /dev/null +++ b/Samples/Logging/cppwinrt/SampleConfiguration.cpp @@ -0,0 +1,32 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "pch.h" +#include "SampleConfiguration.h" +#include "MainPage.h" +#include + +using namespace winrt; +using namespace winrt::SDKTemplate::implementation; + +winrt::hstring +MainPage::FEATURE_NAME() +{ + return L"Logging C++/WinRT Sample"; +} + +Windows::Foundation::Collections::IVector +MainPage::scenariosInner = winrt::single_threaded_observable_vector( +{ + Scenario{ L"LoggingChannel", winrt::xaml_typename() }, + Scenario{ L"LoggingSession", winrt::xaml_typename() }, + Scenario{ L"FileLoggingSession", winrt::xaml_typename() }, +}); diff --git a/Samples/Logging/cppwinrt/SampleConfiguration.h b/Samples/Logging/cppwinrt/SampleConfiguration.h new file mode 100644 index 0000000000..2c10b7e8b8 --- /dev/null +++ b/Samples/Logging/cppwinrt/SampleConfiguration.h @@ -0,0 +1,16 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +namespace winrt::SDKTemplate +{ +} diff --git a/Samples/Logging/cppwinrt/Scenario1.cpp b/Samples/Logging/cppwinrt/Scenario1.cpp new file mode 100644 index 0000000000..61eb01be70 --- /dev/null +++ b/Samples/Logging/cppwinrt/Scenario1.cpp @@ -0,0 +1,35 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "pch.h" +#include "Scenario1.h" +#include "Scenario1.g.cpp" +#include "LoggingChannelScenario.h" + +using namespace winrt; +using namespace winrt::SDKTemplate::implementation; + +Scenario1::Scenario1() +{ + InitializeComponent(); +} + +void Scenario1::DoWin81Mode(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::RoutedEventArgs const&) +{ + LoggingChannelScenario().LogWithWin81Constructor(); + MainPage::Current().NotifyUser(L"Complete: Windows 8 mode", NotifyType::StatusMessage); +} + +void Scenario1::DoWin10Mode(IInspectable const&, Windows::UI::Xaml::RoutedEventArgs const&) +{ + LoggingChannelScenario().LogWithWin10Constructor(); + MainPage::Current().NotifyUser(L"Complete: Windows 10 mode", NotifyType::StatusMessage); +} diff --git a/Samples/Logging/cppwinrt/Scenario1.h b/Samples/Logging/cppwinrt/Scenario1.h new file mode 100644 index 0000000000..2048b4df7e --- /dev/null +++ b/Samples/Logging/cppwinrt/Scenario1.h @@ -0,0 +1,39 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +#include "Scenario1.g.h" + +namespace winrt::SDKTemplate::implementation +{ + struct Scenario1 : Scenario1T + { + Scenario1(); + + void + DoWin81Mode( + Windows::Foundation::IInspectable const& sender, + Windows::UI::Xaml::RoutedEventArgs const& args); + + void + DoWin10Mode( + Windows::Foundation::IInspectable const& sender, + Windows::UI::Xaml::RoutedEventArgs const& args); + }; +} + +namespace winrt::SDKTemplate::factory_implementation +{ + struct Scenario1 : Scenario1T + { + }; +} diff --git a/Samples/Logging/cppwinrt/Scenario2.cpp b/Samples/Logging/cppwinrt/Scenario2.cpp new file mode 100644 index 0000000000..df29cb6506 --- /dev/null +++ b/Samples/Logging/cppwinrt/Scenario2.cpp @@ -0,0 +1,183 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "pch.h" +#include "Scenario2.h" +#include "Scenario2.g.cpp" +#include "LoggingScenarioHelpers.h" + +using namespace winrt; +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Windows::UI::Xaml::Controls; +using namespace winrt::Windows::UI::Xaml::Navigation; +using namespace winrt::SDKTemplate::implementation; + +Scenario2::Scenario2() +{ + InitializeComponent(); +} + +fire_and_forget +Scenario2::DoScenario(IInspectable const&, RoutedEventArgs const&) +{ + MainPage::Current().NotifyUser(L"Scenario running...", NotifyType::StatusMessage); + DoScenarioButton().IsEnabled(false); + + // Run the scenario asynchronously so the UI can update. + co_await resume_background(); + co_await _instance.DoScenarioAsync(); + + // After the scenario completes, re-enable UI controls and display a message. + co_await resume_foreground(Dispatcher()); + DoScenarioButton().IsEnabled(true); + MainPage::Current().NotifyUser(L"Scenario finished.", NotifyType::StatusMessage); +} + +void +Scenario2::EnableDisableLogging(IInspectable const&, RoutedEventArgs const&) +{ + if (_instance.LoggingEnabled()) + { + MainPage::Current().NotifyUser(L"Disabling logging...", NotifyType::StatusMessage); + } + else + { + MainPage::Current().NotifyUser(L"Enabling logging...", NotifyType::StatusMessage); + } + + _instance.ToggleLoggingEnabledDisabled(); + UpdateControls(); + + if (_instance.LoggingEnabled()) + { + MainPage::Current().NotifyUser(L"Logging enabled.", NotifyType::StatusMessage); + } + else + { + MainPage::Current().NotifyUser(L"Logging disabled.", NotifyType::StatusMessage); + } +} + +void +Scenario2::OnNavigatedTo(NavigationEventArgs const&) +{ + _statusChangedToken = _instance.StatusChanged({ this, &Scenario2::OnStatusChanged }); + UpdateControls(); +} + +void +Scenario2::OnNavigatedFrom(NavigationEventArgs const&) +{ + _instance.StatusChanged(_statusChangedToken); +} + +void +Scenario2::UpdateControls() +{ + if (_instance.LoggingEnabled()) + { + InputTextBlock1().Text( + L"Logging is enabled. Click 'Disable Logging' to disable logging. With logging enabled, you can click 'Log Messages' to use the logging API to generate log files."); + EnableDisableLoggingButton().Content(box_value(L"Disable Logging")); + + if (_instance.IsBusy()) + { + EnableDisableLoggingButton().IsEnabled(false); + DoScenarioButton().IsEnabled(false); + } + else + { + EnableDisableLoggingButton().IsEnabled(true); + DoScenarioButton().IsEnabled(true); + } + } + else + { + InputTextBlock1().Text( + L"Logging is disabled. Click 'Enable Logging' to enable logging. After you enable logging you can click 'Log Messages' to use the logging API to generate log files."); + EnableDisableLoggingButton().Content(box_value(L"Enable Logging")); + DoScenarioButton().IsEnabled(false); + EnableDisableLoggingButton().IsEnabled(!_instance.IsBusy()); + } +} + +void +Scenario2::AddMessage(LPCWSTR message) +{ + TextBox statusMessageList = StatusMessageList(); + std::wstring statusText(statusMessageList.Text()); + statusText += message; + statusText += L"\r\n"; + statusMessageList.Text(statusText); + statusMessageList.Select(static_cast(statusText.size()), 0); + + ScrollViewer viewer = FindScrollViewer(statusMessageList); + if (viewer != nullptr) + { + viewer.ChangeView(nullptr, statusMessageList.ActualHeight(), nullptr); + } +} + +fire_and_forget +Scenario2::OnStatusChanged(SDKTemplate::LoggingScenarioEventArgs args) +{ + // Switch to UI thread. + co_await resume_foreground(Dispatcher()); + + switch (args.Type()) + { + case LoggingScenarioEventType::BusyStatusChanged: + UpdateControls(); + break; + + case LoggingScenarioEventType::LogFileGenerated: + { + hstring logFileFullPath = args.LogFileFullPath(); + if (logFileFullPath.empty()) + { + AddMessage(L"LogFileGenerated: none, nothing logged since saving the last file."); + } + else + { + // Add a message to the UI indicating a new log file was created. + std::wstring fullPath(logFileFullPath); + std::wstring directoryName; + std::wstring fileName; + std::size_t lastSlashPos = fullPath.find_last_of(L'\\'); + if (lastSlashPos > 0 && lastSlashPos < fullPath.size() - 1) + { + directoryName = fullPath.substr(0, lastSlashPos); + fileName = fullPath.substr(lastSlashPos + 1, fullPath.size() - (lastSlashPos + 1)); + } + + ViewLogInfo().Text( + L"Log folder: \"" + directoryName + L"\"\r\n" + L"- To view with tracerpt: tracerpt.exe \"" + fullPath + L"\" -of XML -o LogFile.xml\r\n" + L"- To view with Windows Performance Toolkit (WPT):\n" + L" xperf -merge \"" + fullPath + "\" merged.etl\n" + L" wpa.exe merged.etl"); + AddMessage((L"LogFileGenerated: " + fileName).c_str()); + } + } + break; + + case LoggingScenarioEventType::LoggingEnabledDisabled: + AddMessage(args.Enabled() + ? L"Logging has been enabled." + : L"Logging has been disabled."); + break; + + default: + AddMessage(L"Unrecognized event."); + break; + } +} diff --git a/Samples/Logging/cppwinrt/Scenario2.h b/Samples/Logging/cppwinrt/Scenario2.h new file mode 100644 index 0000000000..c76416fbc5 --- /dev/null +++ b/Samples/Logging/cppwinrt/Scenario2.h @@ -0,0 +1,62 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +#include "Scenario2.g.h" +#include "LoggingSessionScenario.h" + +namespace winrt::SDKTemplate::implementation +{ + struct Scenario2 : Scenario2T + { + Scenario2(); + + fire_and_forget + DoScenario( + Windows::Foundation::IInspectable const& sender, + Windows::UI::Xaml::RoutedEventArgs const& args); + + void + EnableDisableLogging( + Windows::Foundation::IInspectable const& sender, + Windows::UI::Xaml::RoutedEventArgs const& args); + + void + OnNavigatedTo( + Windows::UI::Xaml::Navigation::NavigationEventArgs const& args); + + void + OnNavigatedFrom( + Windows::UI::Xaml::Navigation::NavigationEventArgs const& args); + + private: + + void + UpdateControls(); + + void + AddMessage(_In_ LPCWSTR message); + + fire_and_forget + OnStatusChanged(SDKTemplate::LoggingScenarioEventArgs args); + + LoggingSessionScenario& _instance = LoggingSessionScenario::Instance(); + event_token _statusChangedToken; + }; +} + +namespace winrt::SDKTemplate::factory_implementation +{ + struct Scenario2 : Scenario2T + { + }; +} diff --git a/Samples/Logging/cppwinrt/Scenario3.cpp b/Samples/Logging/cppwinrt/Scenario3.cpp new file mode 100644 index 0000000000..d9925d39ab --- /dev/null +++ b/Samples/Logging/cppwinrt/Scenario3.cpp @@ -0,0 +1,204 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "pch.h" +#include "Scenario3.h" +#include "Scenario3.g.cpp" +#include "LoggingScenarioHelpers.h" + +using namespace winrt; +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Windows::UI::Xaml::Controls; +using namespace winrt::Windows::UI::Xaml::Navigation; +using namespace winrt::SDKTemplate::implementation; + +Scenario3::Scenario3() +{ + InitializeComponent(); +} + +fire_and_forget +Scenario3::DoScenario(IInspectable const&, RoutedEventArgs const&) +{ + MainPage::Current().NotifyUser(L"Scenario running...", NotifyType::StatusMessage); + DoScenarioButton().IsEnabled(false); + + // Run the scenario asynchronously so the UI can update. + co_await resume_background(); + co_await _instance.DoScenarioAsync(); + + // After the scenario completes, re-enable UI controls and display a message. + co_await resume_foreground(Dispatcher()); + DoScenarioButton().IsEnabled(true); + MainPage::Current().NotifyUser(L"Scenario finished.", NotifyType::StatusMessage); +} + +fire_and_forget +Scenario3::EnableDisableLogging(IInspectable const&, RoutedEventArgs const&) +{ + if (_instance.LoggingEnabled()) + { + MainPage::Current().NotifyUser(L"Disabling logging...", NotifyType::StatusMessage); + } + else + { + MainPage::Current().NotifyUser(L"Enabling logging...", NotifyType::StatusMessage); + } + + co_await _instance.ToggleLoggingEnabledDisabledAsync(); + + co_await resume_foreground(Dispatcher()); + + UpdateControls(); + + if (_instance.LoggingEnabled()) + { + MainPage::Current().NotifyUser(L"Logging enabled.", NotifyType::StatusMessage); + } + else + { + MainPage::Current().NotifyUser(L"Logging disabled.", NotifyType::StatusMessage); + } +} + +void +Scenario3::OnNavigatedTo(NavigationEventArgs const&) +{ + _statusChangedToken = _instance.StatusChanged({ this, &Scenario3::OnStatusChanged }); + UpdateControls(); +} + +void +Scenario3::OnNavigatedFrom(NavigationEventArgs const&) +{ + _instance.StatusChanged(_statusChangedToken); + UpdateControls(); +} + +void +Scenario3::UpdateControls() +{ + if (_instance.LoggingEnabled()) + { + InputTextBlock1().Text( + L"Logging is enabled. Click 'Disable Logging' to disable logging. With logging enabled, you can click 'Log Messages' to use the logging API to generate log files."); + EnableDisableLoggingButton().Content(box_value(L"Disable Logging")); + + if (_instance.IsBusy()) + { + EnableDisableLoggingButton().IsEnabled(false); + DoScenarioButton().IsEnabled(false); + } + else + { + EnableDisableLoggingButton().IsEnabled(true); + DoScenarioButton().IsEnabled(true); + } + } + else + { + InputTextBlock1().Text( + L"Logging is disabled. Click 'Enable Logging' to enable logging. After you enable logging you can click 'Log Messages' to use the logging API to generate log files."); + EnableDisableLoggingButton().Content(box_value(L"Enable Logging")); + DoScenarioButton().IsEnabled(false); + EnableDisableLoggingButton().IsEnabled(!_instance.IsBusy()); + } +} + +void +Scenario3::AddMessage(_In_ LPCWSTR message) +{ + TextBox statusMessageList = StatusMessageList(); + std::wstring statusText(statusMessageList.Text()); + statusText += message; + statusText += L"\r\n"; + statusMessageList.Text(statusText); + statusMessageList.Select(static_cast(statusText.size()), 0); + + ScrollViewer viewer = FindScrollViewer(statusMessageList); + if (viewer != nullptr) + { + viewer.ChangeView(nullptr, statusMessageList.ActualHeight(), nullptr); + } +} + +void +Scenario3::AddLogFileMessage(_In_ LPCWSTR message, _In_opt_ LPCWSTR logFileFullPath) +{ + std::wstring finalMessage = message; + + if (logFileFullPath == nullptr || logFileFullPath[0] == 0) + { + finalMessage += L": none, nothing logged since saving the last file."; + } + else + { + // Add a message to the UI indicating a new log file was created. + std::wstring fullPath(logFileFullPath); + std::wstring directoryName; + std::wstring fileName; + std::size_t lastSlashPos = fullPath.find_last_of(L'\\'); + if (lastSlashPos > 0 && lastSlashPos < fullPath.size() - 1) + { + directoryName = fullPath.substr(0, lastSlashPos); + fileName = fullPath.substr(lastSlashPos + 1, fullPath.size() - (lastSlashPos + 1)); + } + + ViewLogInfo().Text( + L"Log folder: \"" + directoryName + L"\"\r\n" + L"- To view with tracerpt: tracerpt.exe \"" + fullPath + L"\" -of XML -o LogFile.xml\r\n" + L"- To view with Windows Performance Toolkit (WPT):\n" + L" xperf -merge \"" + fullPath + "\" merged.etl\n" + L" wpa.exe merged.etl"); + + finalMessage += L": "; + finalMessage += fileName; + } + + AddMessage(finalMessage.c_str()); +} + +fire_and_forget +Scenario3::OnStatusChanged(SDKTemplate::LoggingScenarioEventArgs args) +{ + // Switch to UI thread. + co_await resume_foreground(Dispatcher()); + + switch (args.Type()) + { + case LoggingScenarioEventType::BusyStatusChanged: + UpdateControls(); + break; + + case LoggingScenarioEventType::LogFileGenerated: + AddLogFileMessage(L"LogFileGenerated", args.LogFileFullPath().c_str()); + break; + + case LoggingScenarioEventType::LogFileGeneratedAtSuspend: + AddLogFileMessage(L"Log file at suspend", args.LogFileFullPath().c_str()); + break; + + case LoggingScenarioEventType::LogFileGeneratedAtDisable: + AddLogFileMessage(L"Log file at disable", args.LogFileFullPath().c_str()); + break; + + case LoggingScenarioEventType::LoggingEnabledDisabled: + AddMessage(args.Enabled() + ? L"Logging has been enabled." + : L"Logging has been disabled."); + break; + + default: + AddMessage(L"Unrecognized event."); + break; + } +} diff --git a/Samples/Logging/cppwinrt/Scenario3.h b/Samples/Logging/cppwinrt/Scenario3.h new file mode 100644 index 0000000000..eca2391438 --- /dev/null +++ b/Samples/Logging/cppwinrt/Scenario3.h @@ -0,0 +1,67 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +#include "Scenario3.g.h" +#include "FileLoggingSessionScenario.h" + +namespace winrt::SDKTemplate::implementation +{ + struct Scenario3 : Scenario3T + { + Scenario3(); + + fire_and_forget + DoScenario( + Windows::Foundation::IInspectable const& sender, + Windows::UI::Xaml::RoutedEventArgs const& args); + + fire_and_forget + EnableDisableLogging( + Windows::Foundation::IInspectable const& sender, + Windows::UI::Xaml::RoutedEventArgs const& args); + + void + OnNavigatedTo( + Windows::UI::Xaml::Navigation::NavigationEventArgs const& args); + + void + OnNavigatedFrom( + Windows::UI::Xaml::Navigation::NavigationEventArgs const& args); + + private: + + void + UpdateControls(); + + void + AddMessage(_In_ LPCWSTR message); + + void + AddLogFileMessage( + _In_ LPCWSTR message, + _In_opt_ LPCWSTR logFileFullPath); + + fire_and_forget + OnStatusChanged(SDKTemplate::LoggingScenarioEventArgs args); + + FileLoggingSessionScenario& _instance = FileLoggingSessionScenario::Instance(); + event_token _statusChangedToken; + }; +} + +namespace winrt::SDKTemplate::factory_implementation +{ + struct Scenario3 : Scenario3T + { + }; +} diff --git a/Samples/Logging/cppwinrt/packages.config b/Samples/Logging/cppwinrt/packages.config new file mode 100644 index 0000000000..68fd1b237d --- /dev/null +++ b/Samples/Logging/cppwinrt/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Samples/Logging/cppwinrt/pch.cpp b/Samples/Logging/cppwinrt/pch.cpp new file mode 100644 index 0000000000..bcb5590be1 --- /dev/null +++ b/Samples/Logging/cppwinrt/pch.cpp @@ -0,0 +1 @@ +#include "pch.h" diff --git a/Samples/Logging/cppwinrt/pch.h b/Samples/Logging/cppwinrt/pch.h new file mode 100644 index 0000000000..3af35202e3 --- /dev/null +++ b/Samples/Logging/cppwinrt/pch.h @@ -0,0 +1,31 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +#include +#include "winrt/Windows.Foundation.h" +#include "winrt/Windows.Foundation.Collections.h" +#include "winrt/Windows.Foundation.Diagnostics.h" +#include "winrt/Windows.Storage.h" +#include "winrt/Windows.ApplicationModel.Activation.h" +#include "winrt/Windows.System.h" +#include "winrt/Windows.UI.Core.h" +#include "winrt/Windows.UI.Xaml.h" +#include "winrt/Windows.UI.Xaml.Automation.Peers.h" +#include "winrt/Windows.UI.Xaml.Controls.h" +#include "winrt/Windows.UI.Xaml.Controls.Primitives.h" +#include "winrt/Windows.UI.Xaml.Documents.h" +#include "winrt/Windows.UI.Xaml.Interop.h" +#include "winrt/Windows.UI.Xaml.Markup.h" +#include "winrt/Windows.UI.Xaml.Media.h" +#include "winrt/Windows.UI.Xaml.Navigation.h" +#include diff --git a/SharedContent/cs/DeviceHelpers.cs b/SharedContent/cs/DeviceHelpers.cs index 34762e0307..1117478c0c 100644 --- a/SharedContent/cs/DeviceHelpers.cs +++ b/SharedContent/cs/DeviceHelpers.cs @@ -50,6 +50,18 @@ public static async Task GetFirstDeviceAsync(string selector, Func + { + // We don't do anything here, but this event needs to be handled to enable realtime updates. + // See https://aka.ms/devicewatcher_added. + }; + + watcher.Updated += (DeviceWatcher sender, DeviceInformationUpdate args) => + { + // We don't do anything here, but this event needs to be handled to enable realtime updates. + // See https://aka.ms/devicewatcher_added. + }; + watcher.Start(); // Wait for enumeration to complete or for a device to be found.
Capabilities Custom capabilities In-process component authoringIn-process component authoring
In-process component authoring Out-of-process component authoring Version adaptive code