Skip to content

Commit

Permalink
Add Loader Snaps to DevDiagnostics's insights (#3815)
Browse files Browse the repository at this point in the history
* Updates

* Fix build break, manifest whitespace

* PR feedback

* More PR feedback

* PR feedback

* PR feedback
  • Loading branch information
timkur authored Sep 10, 2024
1 parent fd88e8e commit c9745cc
Show file tree
Hide file tree
Showing 38 changed files with 1,690 additions and 753 deletions.
47 changes: 47 additions & 0 deletions DevHome.sln
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestTools", "TestTools", "{
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestDumpAnalyzer", "tools\DevDiagnostics\TestTools\TestDumpAnalyzer\TestDumpAnalyzer.csproj", "{29963C5C-4CB6-4406-850A-99503CB87081}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utilities", "Utilities", "{81AB1B86-AE3C-4E5B-8918-CA6CCED95266}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EnableLoaderSnaps", "tools\DevDiagnostics\Utilities\EnableLoaderSnaps\EnableLoaderSnaps.csproj", "{EB097CCA-D4B6-4C99-9EB8-161F26345562}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestLoadFailure", "tools\DevDiagnostics\TestTools\TestLoadFailure\TestLoadFailure.vcxproj", "{F196A38B-190C-4B00-813D-DC0B7AD24CB7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestServiceCaller", "tools\DevDiagnostics\TestTools\TestServiceCaller\TestServiceCaller.csproj", "{024CB70C-3449-4204-9171-A25C6C25783D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevHome.Database", "database\DevHome.Database\DevHome.Database.csproj", "{01CADE81-7333-4E24-A39B-95B7E58D4503}"
Expand Down Expand Up @@ -1255,6 +1261,42 @@ Global
{29963C5C-4CB6-4406-850A-99503CB87081}.Release|x64.Build.0 = Release|x64
{29963C5C-4CB6-4406-850A-99503CB87081}.Release|x86.ActiveCfg = Release|x86
{29963C5C-4CB6-4406-850A-99503CB87081}.Release|x86.Build.0 = Release|x86
{EB097CCA-D4B6-4C99-9EB8-161F26345562}.Debug_FailFast|arm64.ActiveCfg = Debug_FailFast|ARM64
{EB097CCA-D4B6-4C99-9EB8-161F26345562}.Debug_FailFast|arm64.Build.0 = Debug_FailFast|ARM64
{EB097CCA-D4B6-4C99-9EB8-161F26345562}.Debug_FailFast|x64.ActiveCfg = Debug_FailFast|x64
{EB097CCA-D4B6-4C99-9EB8-161F26345562}.Debug_FailFast|x64.Build.0 = Debug_FailFast|x64
{EB097CCA-D4B6-4C99-9EB8-161F26345562}.Debug_FailFast|x86.ActiveCfg = Debug_FailFast|x86
{EB097CCA-D4B6-4C99-9EB8-161F26345562}.Debug_FailFast|x86.Build.0 = Debug_FailFast|x86
{EB097CCA-D4B6-4C99-9EB8-161F26345562}.Debug|arm64.ActiveCfg = Debug|ARM64
{EB097CCA-D4B6-4C99-9EB8-161F26345562}.Debug|arm64.Build.0 = Debug|ARM64
{EB097CCA-D4B6-4C99-9EB8-161F26345562}.Debug|x64.ActiveCfg = Debug|x64
{EB097CCA-D4B6-4C99-9EB8-161F26345562}.Debug|x64.Build.0 = Debug|x64
{EB097CCA-D4B6-4C99-9EB8-161F26345562}.Debug|x86.ActiveCfg = Debug|x86
{EB097CCA-D4B6-4C99-9EB8-161F26345562}.Debug|x86.Build.0 = Debug|x86
{EB097CCA-D4B6-4C99-9EB8-161F26345562}.Release|arm64.ActiveCfg = Release|ARM64
{EB097CCA-D4B6-4C99-9EB8-161F26345562}.Release|arm64.Build.0 = Release|ARM64
{EB097CCA-D4B6-4C99-9EB8-161F26345562}.Release|x64.ActiveCfg = Release|x64
{EB097CCA-D4B6-4C99-9EB8-161F26345562}.Release|x64.Build.0 = Release|x64
{EB097CCA-D4B6-4C99-9EB8-161F26345562}.Release|x86.ActiveCfg = Release|x86
{EB097CCA-D4B6-4C99-9EB8-161F26345562}.Release|x86.Build.0 = Release|x86
{F196A38B-190C-4B00-813D-DC0B7AD24CB7}.Debug_FailFast|arm64.ActiveCfg = Debug|x64
{F196A38B-190C-4B00-813D-DC0B7AD24CB7}.Debug_FailFast|arm64.Build.0 = Debug|x64
{F196A38B-190C-4B00-813D-DC0B7AD24CB7}.Debug_FailFast|x64.ActiveCfg = Debug|x64
{F196A38B-190C-4B00-813D-DC0B7AD24CB7}.Debug_FailFast|x64.Build.0 = Debug|x64
{F196A38B-190C-4B00-813D-DC0B7AD24CB7}.Debug_FailFast|x86.ActiveCfg = Debug|Win32
{F196A38B-190C-4B00-813D-DC0B7AD24CB7}.Debug_FailFast|x86.Build.0 = Debug|Win32
{F196A38B-190C-4B00-813D-DC0B7AD24CB7}.Debug|arm64.ActiveCfg = Debug|x64
{F196A38B-190C-4B00-813D-DC0B7AD24CB7}.Debug|arm64.Build.0 = Debug|x64
{F196A38B-190C-4B00-813D-DC0B7AD24CB7}.Debug|x64.ActiveCfg = Debug|x64
{F196A38B-190C-4B00-813D-DC0B7AD24CB7}.Debug|x64.Build.0 = Debug|x64
{F196A38B-190C-4B00-813D-DC0B7AD24CB7}.Debug|x86.ActiveCfg = Debug|Win32
{F196A38B-190C-4B00-813D-DC0B7AD24CB7}.Debug|x86.Build.0 = Debug|Win32
{F196A38B-190C-4B00-813D-DC0B7AD24CB7}.Release|arm64.ActiveCfg = Release|x64
{F196A38B-190C-4B00-813D-DC0B7AD24CB7}.Release|arm64.Build.0 = Release|x64
{F196A38B-190C-4B00-813D-DC0B7AD24CB7}.Release|x64.ActiveCfg = Release|x64
{F196A38B-190C-4B00-813D-DC0B7AD24CB7}.Release|x64.Build.0 = Release|x64
{F196A38B-190C-4B00-813D-DC0B7AD24CB7}.Release|x86.ActiveCfg = Release|Win32
{F196A38B-190C-4B00-813D-DC0B7AD24CB7}.Release|x86.Build.0 = Release|Win32
{024CB70C-3449-4204-9171-A25C6C25783D}.Debug_FailFast|arm64.ActiveCfg = Debug_FailFast|ARM64
{024CB70C-3449-4204-9171-A25C6C25783D}.Debug_FailFast|arm64.Build.0 = Debug_FailFast|ARM64
{024CB70C-3449-4204-9171-A25C6C25783D}.Debug_FailFast|x64.ActiveCfg = Debug_FailFast|x64
Expand Down Expand Up @@ -1383,6 +1425,9 @@ Global
{17E96025-8251-4D62-BFFB-C571F728B66B} = {E6BC5785-34E5-4A19-81A4-2CC8A37FD63F}
{7857275B-7970-4C26-B092-FF62EA890B1E} = {DB3D0F2C-1A7F-44B4-B408-B21A56212985}
{29963C5C-4CB6-4406-850A-99503CB87081} = {7857275B-7970-4C26-B092-FF62EA890B1E}
{81AB1B86-AE3C-4E5B-8918-CA6CCED95266} = {DB3D0F2C-1A7F-44B4-B408-B21A56212985}
{EB097CCA-D4B6-4C99-9EB8-161F26345562} = {81AB1B86-AE3C-4E5B-8918-CA6CCED95266}
{F196A38B-190C-4B00-813D-DC0B7AD24CB7} = {7857275B-7970-4C26-B092-FF62EA890B1E}
{024CB70C-3449-4204-9171-A25C6C25783D} = {7857275B-7970-4C26-B092-FF62EA890B1E}
{567A82BE-7E9E-4D95-AF45-4EE8D57FE16D} = {A972EC5B-FC61-4964-A6FF-F9633EB75DFD}
{82BD8133-F1D4-4383-BC4F-12EFAE1AFF91} = {567A82BE-7E9E-4D95-AF45-4EE8D57FE16D}
Expand All @@ -1396,5 +1441,7 @@ Global
build\cppversion\version.vcxitems*{13f1f098-262b-432d-977c-f710e6826c5c}*SharedItemsImports = 5
build\cppversion\version.vcxitems*{60e0fd98-5396-436d-bab7-187a853a5dc6}*SharedItemsImports = 5
build\cppversion\version.vcxitems*{8a0ad1a7-29e1-47e1-9579-91f2b460518f}*SharedItemsImports = 5
build\cppversion\version.vcxitems*{caac0cdf-9ab8-4f43-a3eb-38d785af5725}*SharedItemsImports = 5
build\cppversion\version.vcxitems*{eb097cca-d4b6-4c99-9eb8-161f26345562}*SharedItemsImports = 5
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(SolutionDir)ToolingVersions.props" />
<Import Project="$(SolutionDir)Directory.CppBuild.props" />
<PropertyGroup>
<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
</PropertyGroup>
Expand Down
7 changes: 6 additions & 1 deletion service/DevHome.Service/DevHome.Service.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk.Worker">
<Import Project="$(SolutionDir)ToolingVersions.props" />
<Import Project="$(SolutionDir)Directory.CppBuild.props" />
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>DevHome.Service</RootNamespace>
Expand All @@ -11,6 +10,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.9" GeneratePathProperty="true" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.0" />
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.106">
Expand All @@ -19,6 +19,11 @@
</PackageReference>
</ItemGroup>

<ItemGroup>
<None Remove="$(PkgMicrosoft_Diagnostics_Tracing_TraceEvent)\lib\netstandard2.0\TraceReloggerLib.dll" />
<None Remove="$(PkgMicrosoft_Diagnostics_Tracing_TraceEvent)\lib\netstandard2.0\Dia2Lib.dll" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\DevHome.Service.Projection\DevHome.Service.Projection.csproj" />
</ItemGroup>
Expand Down
81 changes: 76 additions & 5 deletions service/DevHome.Service/DevHomeService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,95 @@

using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.Diagnostics.Tracing.Parsers;
using Microsoft.Diagnostics.Tracing.Session;
using Windows.Win32.Foundation;

namespace DevHome.Service.Runtime;

[ComVisible(true)]
public class DevHomeService : IDevHomeService
public class DevHomeService : IDevHomeService, IDisposable
{
private readonly Process _owner;

public event MissingFileProcessLaunchFailureHandler? MissingFileProcessLaunchFailure;

private TraceEventSession? _etwSession;

public DevHomeService()
{
Process myCaller = ComHelpers.GetClientProcess();

ComHelpers.VerifyCaller(myCaller);

_owner = myCaller;
_owner.EnableRaisingEvents = true;

// Track our caller process
ServiceLifetimeController.RegisterProcess(myCaller);
ServiceLifetimeController.ServiceStop += ServiceLifetimeController_ServiceStop;
ServiceLifetimeController.RegisterProcess(_owner);

var crashDumpAnalyzerThread = new Thread(() =>
{
KernelEventETWListener();
});
crashDumpAnalyzerThread.Name = "KernelEventETWListenerThread";
crashDumpAnalyzerThread.Start();

_owner.Exited += Owner_Exited;
}

private void ServiceLifetimeController_ServiceStop()
{
// Be sure to stop our ETW session when we exit. It's possible this gets called multiple times
// if we have multiple instances of our object... that's ok.
_etwSession?.Stop();
}

private void Owner_Exited(object? sender, EventArgs e)
{
// The owner of our object has exited. We'll stop listening to ETW events from this channel, but keep it open in case there are
// other listeners of the channel in this process.
_etwSession?.Source.StopProcessing();
}

private void KernelEventETWListener()
{
_etwSession = new TraceEventSession("DevHome.Service.KernelEventETWListenerSession");

// Enable the kernel provider to look for processes exiting with non-zero exit codes
_etwSession.EnableKernelProvider(KernelTraceEventParser.Keywords.Process);

_etwSession.Source.Kernel.ProcessStop += data =>
{
// Only return data for processes in session 0 or the caller's session (don't let one session spy on another session)
if (data.SessionID == 0 || data.SessionID == _owner.SessionId)
{
if (data.ExitStatus == NTSTATUS.STATUS_DLL_NOT_FOUND || data.ExitStatus == (int)WIN32_ERROR.ERROR_MOD_NOT_FOUND)
{
MissingFileProcessLaunchFailureInfo info = default(MissingFileProcessLaunchFailureInfo);
info.processName = data.ImageFileName;
info.pid = data.ProcessID;
info.exitCode = data.ExitStatus;
try
{
MissingFileProcessLaunchFailure?.Invoke(info);
}
catch (Exception)
{
// We don't want to crash the process if the event handler throws an exception
}
}
}
};

_etwSession.Source.Process();
}

public int GetNumber()
public void Dispose()
{
return 42;
_etwSession?.Source.StopProcessing();
_etwSession?.Dispose();
GC.SuppressFinalize(this);
}
}
2 changes: 2 additions & 0 deletions service/DevHome.Service/NativeMethods.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ E_NOINTERFACE
GLOBALOPT_EH_VALUES
GLOBALOPT_PROPERTIES
GLOBALOPT_RO_FLAGS
STATUS_DLL_NOT_FOUND
WIN32_ERROR

34 changes: 22 additions & 12 deletions service/DevHome.Service/ServiceLifetimeController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@

namespace DevHome.Service;

internal delegate void ServiceStopEvent();

internal sealed class ServiceLifetimeController
{
private static readonly List<Process> _processes = new();

internal static event ServiceStopEvent? ServiceStop;

public static void RegisterProcess(Process p)
{
lock (_processes)
Expand All @@ -23,18 +27,24 @@ public static void RegisterProcess(Process p)
{
_processes.Add(p);
p.EnableRaisingEvents = true;
p.Exited += (sender, e) =>
{
lock (_processes)
{
_processes.Remove(p);
if (_processes.Count == 0)
{
// It's ok to stop the service now
WindowsBackgroundService.Stop();
}
}
};
p.Exited += ProcessExited;
}
}
}

private static void ProcessExited(object? sender, EventArgs e)
{
Process? p = sender as Process;
Debug.Assert(p is not null, "What is this object?");

lock (_processes)
{
_processes.Remove(p);
if (_processes.Count == 0)
{
// It's ok to stop the service now
ServiceStop?.Invoke();
WindowsBackgroundService.Stop();
}
}
}
Expand Down
11 changes: 10 additions & 1 deletion service/Devhome.Service.IDL/DevHome.Service.idl
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,17 @@ import "windows.foundation.idl";

namespace DevHome.Service
{
struct MissingFileProcessLaunchFailureInfo
{
String processName;
Int32 pid;
Int32 exitCode;
};

delegate void MissingFileProcessLaunchFailureHandler(MissingFileProcessLaunchFailureInfo failure);

interface IDevHomeService
{
Int32 GetNumber();
event MissingFileProcessLaunchFailureHandler MissingFileProcessLaunchFailure;
}
}
12 changes: 4 additions & 8 deletions src/Package-Can.appxmanifest
Original file line number Diff line number Diff line change
Expand Up @@ -333,21 +333,17 @@
</uap:SupportedFileTypes>
</uap:FileTypeAssociation>
</uap:Extension>

<desktop6:Extension Category="windows.service" Executable="DevHome.Service.exe" EntryPoint="DevHome.Service">
<desktop6:Service Name="DevHome-Can.Service" StartupType="manual" StartAccount="localSystem" />
</desktop6:Extension>

<com2:Extension Category="windows.comServer">
<com2:ComServer>
<com3:ServiceServer ServiceName="DevHome-Can.Service"
LaunchAndActivationPermission="O:SYG:SYD:(A;;11;;;IU)">
<com3:ServiceServer ServiceName="DevHome-Can.Service" LaunchAndActivationPermission="O:SYG:SYD:(A;;11;;;IU)">
<!-- Grant local activation rights to Interactive Users. -->
<com3:Class Id="0A920C6E-2569-44D1-A6E4-CE9FA44CD2A7" DisplayName="CLSID_DevHomeService"/>
<com3:Class Id="0A920C6E-2569-44D1-A6E4-CE9FA44CD2A7" DisplayName="CLSID_DevHomeService" />
</com3:ServiceServer>
</com2:ComServer>
</com2:Extension>

</Extensions>
</Application>
<Application Id="DevHome.DevDiagnostics" Executable="DevHome.DevDiagnostics.exe" EntryPoint="Windows.FullTrustApplication">
Expand Down Expand Up @@ -410,8 +406,8 @@
<Capabilities>
<rescap:Capability Name="runFullTrust" />
<rescap:Capability Name="unvirtualizedResources" />
<rescap:Capability Name="packagedServices"/>
<rescap:Capability Name="localSystemServices"/>
<rescap:Capability Name="packagedServices" />
<rescap:Capability Name="localSystemServices" />
</Capabilities>
<genTemplate:Metadata>
<genTemplate:Item Name="generator" Value="Template Studio" />
Expand Down
28 changes: 5 additions & 23 deletions src/Package.appxmanifest
Original file line number Diff line number Diff line change
@@ -1,19 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5"
xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
xmlns:genTemplate="http://schemas.microsoft.com/appx/developer/templatestudio"
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
xmlns:com2="http://schemas.microsoft.com/appx/manifest/com/windows10/2"
xmlns:com3="http://schemas.microsoft.com/appx/manifest/com/windows10/3"
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
xmlns:desktop6="http://schemas.microsoft.com/appx/manifest/desktop/windows10/6"
IgnorableNamespaces="uap uap3 uap5 uap10 genTemplate">

<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3" xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5" xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" xmlns:genTemplate="http://schemas.microsoft.com/appx/developer/templatestudio" xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10" xmlns:com2="http://schemas.microsoft.com/appx/manifest/com/windows10/2" xmlns:com3="http://schemas.microsoft.com/appx/manifest/com/windows10/3" xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10" xmlns:desktop6="http://schemas.microsoft.com/appx/manifest/desktop/windows10/6" IgnorableNamespaces="uap uap3 uap5 uap10 genTemplate">
<Extensions>
<Extension Category="windows.activatableClass.proxyStub">
<ProxyStub ClassId="00000355-0000-0000-C000-000000000046">
Expand Down Expand Up @@ -347,21 +333,17 @@
</uap:SupportedFileTypes>
</uap:FileTypeAssociation>
</uap:Extension>

<desktop6:Extension Category="windows.service" Executable="DevHome.Service.exe" EntryPoint="DevHome.Service.Service">
<desktop6:Service Name="DevHome.Service" StartupType="manual" StartAccount="localSystem" />
</desktop6:Extension>

<com2:Extension Category="windows.comServer">
<com2:ComServer>
<com3:ServiceServer ServiceName="DevHome.Service"
LaunchAndActivationPermission="O:SYG:SYD:(A;;11;;;IU)">
<com3:ServiceServer ServiceName="DevHome.Service" LaunchAndActivationPermission="O:SYG:SYD:(A;;11;;;IU)">
<!-- Grant local activation rights to Interactive Users. -->
<com3:Class Id="E8D40232-20A1-4F3B-9C0C-AAA6538698C6" DisplayName="CLSID_DevHomeService"/>
<com3:Class Id="E8D40232-20A1-4F3B-9C0C-AAA6538698C6" DisplayName="CLSID_DevHomeService" />
</com3:ServiceServer>
</com2:ComServer>
</com2:Extension>

</Extensions>
</Application>
<Application Id="DevHome.DevDiagnostics" Executable="DevHome.DevDiagnostics.exe" EntryPoint="Windows.FullTrustApplication">
Expand Down Expand Up @@ -424,8 +406,8 @@
<Capabilities>
<rescap:Capability Name="runFullTrust" />
<rescap:Capability Name="unvirtualizedResources" />
<rescap:Capability Name="packagedServices"/>
<rescap:Capability Name="localSystemServices"/>
<rescap:Capability Name="packagedServices" />
<rescap:Capability Name="localSystemServices" />
</Capabilities>
<genTemplate:Metadata>
<genTemplate:Item Name="generator" Value="Template Studio" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<UserControl
x:Class="DevHome.DevDiagnostics.Controls.ElevatedButtonControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:DevHome.DevDiagnostics.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">

<Grid>
<Button Command="{x:Bind Command, Mode=OneWay}" AutomationProperties.AutomationId="ElevatedButtonControl">
<StackPanel Orientation="Horizontal" Spacing="10">
<SymbolIcon Symbol="Admin" AutomationProperties.AutomationId="ElevatedButtonIcon"/>
<TextBlock Margin="0" Text="{x:Bind Text, Mode=OneWay}" AutomationProperties.AutomationId="ElevatedButtonText"/>
</StackPanel>
</Button>
</Grid>
</UserControl>
Loading

0 comments on commit c9745cc

Please sign in to comment.