From 5a535f0d0fe44c0b360a3f2468ed5324f5152d32 Mon Sep 17 00:00:00 2001 From: Nate Bross Date: Wed, 15 Jul 2020 20:06:41 -0500 Subject: [PATCH] fix(copy): crash on clipboard access from non-focused window --- SharpFM.App/MainPage.xaml | 9 ++- SharpFM.App/MainPage.xaml.cs | 94 ++++++++++++++++--------- SharpFM.App/SharpFM.App.csproj | 4 +- SharpFM.Core/FileMakerClipExtensions.cs | 68 +++++++++++++++--- 4 files changed, 123 insertions(+), 52 deletions(-) diff --git a/SharpFM.App/MainPage.xaml b/SharpFM.App/MainPage.xaml index c3fe2f3..ecdf680 100644 --- a/SharpFM.App/MainPage.xaml +++ b/SharpFM.App/MainPage.xaml @@ -15,7 +15,6 @@ - - + - + Text="{ Binding SelectedClip.XmlData, Mode=OneWay}" />--> \ No newline at end of file diff --git a/SharpFM.App/MainPage.xaml.cs b/SharpFM.App/MainPage.xaml.cs index f408554..d19eacc 100644 --- a/SharpFM.App/MainPage.xaml.cs +++ b/SharpFM.App/MainPage.xaml.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Windows.ApplicationModel.DataTransfer; using Windows.Storage.Streams; +using Windows.UI.Core; using Windows.UI.Popups; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; @@ -32,10 +33,15 @@ public sealed partial class MainPage : Page public string SelectedClipAsCsharp { get; set; } + CoreWindow window; + public MainPage() { InitializeComponent(); + window = CoreWindow.GetForCurrentThread(); + window.Activated += Window_Activated; + Keys = new ObservableCollection(); Layouts = new ObservableCollection(); @@ -51,57 +57,75 @@ public MainPage() Clipboard.ContentChanged += Clipboard_ContentChanged; } - private async void Clipboard_ContentChanged(object sender, object e) + private async Task ProcessClipboard() { - var clip = Clipboard.GetContent(); - - var formats = clip.AvailableFormats.Where(f => f.StartsWith("Mac-", StringComparison.CurrentCultureIgnoreCase)).Distinct(); + try + { + var clip = Clipboard.GetContent(); - Debug.WriteLine($"Formats: {formats.Count()}"); + var formats = clip.AvailableFormats.Where(f => f.StartsWith("Mac-", StringComparison.CurrentCultureIgnoreCase)).Distinct(); - foreach (var format in formats) - { - object clipData = null; + Debug.WriteLine($"Formats: {formats.Count()}"); - try + foreach (var format in formats) { - if (format.Equals("bitmap", StringComparison.CurrentCultureIgnoreCase)) + object clipData = null; + + try { - clipData = await clip.GetBitmapAsync(); + if (format.Equals("bitmap", StringComparison.CurrentCultureIgnoreCase)) + { + clipData = await clip.GetBitmapAsync(); + } + clipData = await clip.GetDataAsync(format); } - clipData = await clip.GetDataAsync(format); - } #pragma warning disable CA1031 // Do not catch general exception types - catch (Exception ex) + catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types - { - Debug.WriteLine(ex.Message); - } + { + Debug.WriteLine(ex.Message); + } - if (!(clipData is IRandomAccessStream dataObj)) - { - // this is some type of clipboard data this program can't handle - continue; - } + if (!(clipData is IRandomAccessStream dataObj)) + { + // this is some type of clipboard data this program can't handle + continue; + } - var stream = dataObj.GetInputStreamAt(0); - IBuffer buff = new Windows.Storage.Streams.Buffer((uint)dataObj.Size); - await stream.ReadAsync(buff, (uint)dataObj.Size, InputStreamOptions.None); - var buffArray = buff.ToArray(); + var stream = dataObj.GetInputStreamAt(0); + IBuffer buff = new Windows.Storage.Streams.Buffer((uint)dataObj.Size); + await stream.ReadAsync(buff, (uint)dataObj.Size, InputStreamOptions.None); + var buffArray = buff.ToArray(); - var fmclip = new FileMakerClip("new-clip", format, buffArray); + var fmclip = new FileMakerClip("new-clip", format, buffArray); - // don't bother adding a duplicate. For some reason entries were getting entered twice per clip - // this is not the most efficient method to detect it, but it works well enough for now - if (Keys.Any(k => k.XmlData == fmclip.XmlData)) - { - continue; - } + // don't bother adding a duplicate. For some reason entries were getting entered twice per clip + // this is not the most efficient method to detect it, but it works well enough for now + if (Keys.Any(k => k.XmlData == fmclip.XmlData)) + { + continue; + } - Keys.Add(fmclip); + Keys.Add(fmclip); + } + } + catch (Exception ex) + { + var md = new MessageDialog(ex.Message + "\r\n" + ex.StackTrace); + await md.ShowAsync(); } } + private async void Window_Activated(CoreWindow sender, WindowActivatedEventArgs args) + { + await ProcessClipboard(); + } + + private void Clipboard_ContentChanged(object sender, object e) + { + window.Activate(); + } + private void Button_Click_1(object sender, RoutedEventArgs e) { var dp = new DataPackage(); @@ -146,6 +170,8 @@ private async void asModelAppBarButton_Click(object sender, RoutedEventArgs e) if (pickerResult == ContentDialogResult.Primary) { // regenerate using the layout picker + SelectedLayout = picker.DialogResult; + var classString = data.CreateClass(SelectedLayout); var dp = new DataPackage(); dp.SetText(classString); diff --git a/SharpFM.App/SharpFM.App.csproj b/SharpFM.App/SharpFM.App.csproj index 259ad6a..ed8513c 100644 --- a/SharpFM.App/SharpFM.App.csproj +++ b/SharpFM.App/SharpFM.App.csproj @@ -11,8 +11,8 @@ SharpFM.App en-US UAP - 10.0.17134.0 - 10.0.16299.0 + 10.0.18362.0 + 10.0.18362.0 14 512 {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} diff --git a/SharpFM.Core/FileMakerClipExtensions.cs b/SharpFM.Core/FileMakerClipExtensions.cs index 4cd15a9..05d9b65 100644 --- a/SharpFM.Core/FileMakerClipExtensions.cs +++ b/SharpFM.Core/FileMakerClipExtensions.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.RegularExpressions; namespace SharpFM.Core { @@ -42,26 +43,54 @@ public static string CreateClass(this FileMakerClip _clip, IEnumerable f // Add System using statement: (using System) @namespace = @namespace.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("System"))); + @namespace = @namespace.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("System.Runtime.Serialization"))); + + var dataContractAttribute = SyntaxFactory.Attribute(SyntaxFactory.ParseName("DataContract")); // Create a class: (class [_clip.Name]) var classDeclaration = SyntaxFactory.ClassDeclaration(_clip.Name); // Add the public modifier: (public class [_clip.Name]) classDeclaration = classDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)); + classDeclaration = classDeclaration.AddAttributeLists(SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(dataContractAttribute))); // add each field from the underling _clip as a public property with the data member attribute - List fieldsToBeAddedAsProperties = new List(_clip.Fields.Count()); + List fieldsToBeAddedAsProperties = new List(_clip.Fields.Count()); // include the field projection foreach (var field in _clip.Fields.Where(fmF => fieldProjectionList.Contains(fmF.Name))) { // filemaker to C# data type mapping - var propertyTypeCSharp = field.DataType - .Replace("Text", "string") - .Replace("Number", "int") - .Replace("Binary", "byte[]") - .Replace("Date", "DateTime") - .Replace("Time", "TimeStamp") - .Replace("TimeStamp", "DateTime"); + var propertyTypeCSharp = string.Empty; + + switch (field.DataType) + { + case "Text": + propertyTypeCSharp = "string"; + break; + case "Number": + propertyTypeCSharp = "int"; + break; + case "Binary": + propertyTypeCSharp = "byte[]"; + break; + case "Date": + propertyTypeCSharp = "DateTime"; + break; + case "Time": + propertyTypeCSharp = "TimeSpan"; + break; + case "TimeStamp": + propertyTypeCSharp = "DateTime"; + break; + default: + propertyTypeCSharp = "string"; + break; + } + + if(field.NotEmpty == false && propertyTypeCSharp != "string") + { + propertyTypeCSharp += "?"; + } var propertyTypeSyntax = SyntaxFactory.ParseTypeName(propertyTypeCSharp); @@ -72,8 +101,9 @@ public static string CreateClass(this FileMakerClip _clip, IEnumerable f .AddAccessorListAccessors( SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))) - .NormalizeWhitespace() - .AddAttributeLists(SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(dataMemberAttribute))); + .NormalizeWhitespace(indentation: "", eol: " ") + .AddAttributeLists(SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(dataMemberAttribute))) + .NormalizeWhitespace(); fieldsToBeAddedAsProperties.Add(propertyDeclaration); } @@ -85,10 +115,26 @@ public static string CreateClass(this FileMakerClip _clip, IEnumerable f @namespace = @namespace.AddMembers(classDeclaration); // Normalize and get code as string. - var code = @namespace.NormalizeWhitespace().ToFullString(); + var code = @namespace.NormalizeWhitespace().ToFullString().FormatAutoPropertiesOnOneLine(); // Output new code to the console. return code; } + + + /// + /// https://stackoverflow.com/a/52339795/86860 + /// + private static readonly Regex AutoPropRegex = new Regex(@"\s*\{\s*get;\s*set;\s*}\s"); + + /// + /// https://stackoverflow.com/a/52339795/86860 + /// + /// Code string to format. + /// The code string with auto properties formatted to a single line + private static string FormatAutoPropertiesOnOneLine(this string str) + { + return AutoPropRegex.Replace(str, " { get; set; }"); + } } }