├── .gitattributes ├── .gitignore ├── CoreTets ├── App.xaml ├── App.xaml.cs ├── AssemblyInfo.cs ├── CoreTest.csproj ├── MainWindow.xaml ├── MainWindow.xaml.cs └── ToastCore.cs ├── LICENSE ├── ToastCore ├── Notification │ ├── ANotificationActivator.cs │ ├── DesktopNotificationHistoryCompat.cs │ ├── DesktopNotificationManagerCompat.cs │ ├── NotificationService.cs │ ├── NotificationUserInput.cs │ └── ToastCommands.cs ├── Properties │ └── AssemblyInfo.cs ├── ToastCore.csproj └── Util │ └── ShellHelper.cs ├── ToastCore[netcore] ├── Notification │ ├── ANotificationActivator.cs │ ├── DesktopNotificationHistoryCompat.cs │ ├── DesktopNotificationManagerCompat.cs │ ├── NotificationService.cs │ ├── NotificationUserInput.cs │ └── ToastCommands.cs ├── RunningObjectTable.cs ├── ShellHelper.cs └── ToastCore[netcore].csproj ├── ToastHelper.sln ├── ToastHelper ├── App.xaml ├── App.xaml.cs ├── AutoStart.cs ├── ClipboardManager.cs ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── QR code.ico ├── ToastHelper.csproj └── ToastManager.cs └── readme.md /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # JustCode is a .NET coding add-in 131 | .JustCode 132 | 133 | # TeamCity is a build add-in 134 | _TeamCity* 135 | 136 | # DotCover is a Code Coverage Tool 137 | *.dotCover 138 | 139 | # AxoCover is a Code Coverage Tool 140 | .axoCover/* 141 | !.axoCover/settings.json 142 | 143 | # Visual Studio code coverage results 144 | *.coverage 145 | *.coveragexml 146 | 147 | # NCrunch 148 | _NCrunch_* 149 | .*crunch*.local.xml 150 | nCrunchTemp_* 151 | 152 | # MightyMoose 153 | *.mm.* 154 | AutoTest.Net/ 155 | 156 | # Web workbench (sass) 157 | .sass-cache/ 158 | 159 | # Installshield output folder 160 | [Ee]xpress/ 161 | 162 | # DocProject is a documentation generator add-in 163 | DocProject/buildhelp/ 164 | DocProject/Help/*.HxT 165 | DocProject/Help/*.HxC 166 | DocProject/Help/*.hhc 167 | DocProject/Help/*.hhk 168 | DocProject/Help/*.hhp 169 | DocProject/Help/Html2 170 | DocProject/Help/html 171 | 172 | # Click-Once directory 173 | publish/ 174 | 175 | # Publish Web Output 176 | *.[Pp]ublish.xml 177 | *.azurePubxml 178 | # Note: Comment the next line if you want to checkin your web deploy settings, 179 | # but database connection strings (with potential passwords) will be unencrypted 180 | *.pubxml 181 | *.publishproj 182 | 183 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 184 | # checkin your Azure Web App publish settings, but sensitive information contained 185 | # in these scripts will be unencrypted 186 | PublishScripts/ 187 | 188 | # NuGet Packages 189 | *.nupkg 190 | # NuGet Symbol Packages 191 | *.snupkg 192 | # The packages folder can be ignored because of Package Restore 193 | **/[Pp]ackages/* 194 | # except build/, which is used as an MSBuild target. 195 | !**/[Pp]ackages/build/ 196 | # Uncomment if necessary however generally it will be regenerated when needed 197 | #!**/[Pp]ackages/repositories.config 198 | # NuGet v3's project.json files produces more ignorable files 199 | *.nuget.props 200 | *.nuget.targets 201 | 202 | # Microsoft Azure Build Output 203 | csx/ 204 | *.build.csdef 205 | 206 | # Microsoft Azure Emulator 207 | ecf/ 208 | rcf/ 209 | 210 | # Windows Store app package directories and files 211 | AppPackages/ 212 | BundleArtifacts/ 213 | Package.StoreAssociation.xml 214 | _pkginfo.txt 215 | *.appx 216 | *.appxbundle 217 | *.appxupload 218 | 219 | # Visual Studio cache files 220 | # files ending in .cache can be ignored 221 | *.[Cc]ache 222 | # but keep track of directories ending in .cache 223 | !?*.[Cc]ache/ 224 | 225 | # Others 226 | ClientBin/ 227 | ~$* 228 | *~ 229 | *.dbmdl 230 | *.dbproj.schemaview 231 | *.jfm 232 | *.pfx 233 | *.publishsettings 234 | orleans.codegen.cs 235 | 236 | # Including strong name files can present a security risk 237 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 238 | #*.snk 239 | 240 | # Since there are multiple workflows, uncomment next line to ignore bower_components 241 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 242 | #bower_components/ 243 | 244 | # RIA/Silverlight projects 245 | Generated_Code/ 246 | 247 | # Backup & report files from converting an old project file 248 | # to a newer Visual Studio version. Backup files are not needed, 249 | # because we have git ;-) 250 | _UpgradeReport_Files/ 251 | Backup*/ 252 | UpgradeLog*.XML 253 | UpgradeLog*.htm 254 | ServiceFabricBackup/ 255 | *.rptproj.bak 256 | 257 | # SQL Server files 258 | *.mdf 259 | *.ldf 260 | *.ndf 261 | 262 | # Business Intelligence projects 263 | *.rdl.data 264 | *.bim.layout 265 | *.bim_*.settings 266 | *.rptproj.rsuser 267 | *- [Bb]ackup.rdl 268 | *- [Bb]ackup ([0-9]).rdl 269 | *- [Bb]ackup ([0-9][0-9]).rdl 270 | 271 | # Microsoft Fakes 272 | FakesAssemblies/ 273 | 274 | # GhostDoc plugin setting file 275 | *.GhostDoc.xml 276 | 277 | # Node.js Tools for Visual Studio 278 | .ntvs_analysis.dat 279 | node_modules/ 280 | 281 | # Visual Studio 6 build log 282 | *.plg 283 | 284 | # Visual Studio 6 workspace options file 285 | *.opt 286 | 287 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 288 | *.vbw 289 | 290 | # Visual Studio LightSwitch build output 291 | **/*.HTMLClient/GeneratedArtifacts 292 | **/*.DesktopClient/GeneratedArtifacts 293 | **/*.DesktopClient/ModelManifest.xml 294 | **/*.Server/GeneratedArtifacts 295 | **/*.Server/ModelManifest.xml 296 | _Pvt_Extensions 297 | 298 | # Paket dependency manager 299 | .paket/paket.exe 300 | paket-files/ 301 | 302 | # FAKE - F# Make 303 | .fake/ 304 | 305 | # CodeRush personal settings 306 | .cr/personal 307 | 308 | # Python Tools for Visual Studio (PTVS) 309 | __pycache__/ 310 | *.pyc 311 | 312 | # Cake - Uncomment if you are using it 313 | # tools/** 314 | # !tools/packages.config 315 | 316 | # Tabs Studio 317 | *.tss 318 | 319 | # Telerik's JustMock configuration file 320 | *.jmconfig 321 | 322 | # BizTalk build output 323 | *.btp.cs 324 | *.btm.cs 325 | *.odx.cs 326 | *.xsd.cs 327 | 328 | # OpenCover UI analysis results 329 | OpenCover/ 330 | 331 | # Azure Stream Analytics local run output 332 | ASALocalRun/ 333 | 334 | # MSBuild Binary and Structured Log 335 | *.binlog 336 | 337 | # NVidia Nsight GPU debugger configuration file 338 | *.nvuser 339 | 340 | # MFractors (Xamarin productivity tool) working folder 341 | .mfractor/ 342 | 343 | # Local History for Visual Studio 344 | .localhistory/ 345 | 346 | # BeatPulse healthcheck temp database 347 | healthchecksdb 348 | 349 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 350 | MigrationBackup/ 351 | 352 | # Ionide (cross platform F# VS Code tools) working folder 353 | .ionide/ 354 | 355 | # Test Files 356 | Test/ -------------------------------------------------------------------------------- /CoreTets/App.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /CoreTets/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace CoreTets { 10 | /// 11 | /// Interaction logic for App.xaml 12 | /// 13 | public partial class App : Application { 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /CoreTets/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | [assembly: ThemeInfo( 4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 5 | //(used if a resource is not found in the page, 6 | // or application resource dictionaries) 7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 8 | //(used if a resource is not found in the page, 9 | // app, or any theme specific resource dictionaries) 10 | )] 11 | -------------------------------------------------------------------------------- /CoreTets/CoreTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | netcoreapp3.1 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /CoreTets/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | Text 22 | Button 23 | Input 24 | 25 | 27 | 30 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /CoreTets/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Windows; 4 | using System.Windows.Controls; 5 | 6 | using CoreTest; 7 | 8 | using ToastCore.Notification; 9 | 10 | namespace CoreTets { 11 | /// 12 | /// Interaction logic for MainWindow.xaml 13 | /// 14 | public partial class MainWindow : Window { 15 | private ToastManagerCore _manager; 16 | private Action _notify; 17 | 18 | public MainWindow() { 19 | InitializeComponent(); 20 | _manager = new ToastManagerCore(); 21 | _manager.Init("ToastTestCore"); 22 | ToastManagerCore.ToastCallback += ToastManager_ToastCallback; 23 | } 24 | 25 | private void Button_Click(object sender, RoutedEventArgs e) { 26 | _notify?.Invoke(); 27 | } 28 | 29 | private void ToastManager_ToastCallback(string app, string arg, List> kvs) { 30 | string res = $"appid : {app} arg : {arg} \n"; 31 | kvs.ForEach(kv => res += $"key : {kv.Key} value : {kv.Value} \n"); 32 | App.Current.Dispatcher.Invoke(() => { 33 | Input.Text = res; 34 | }); 35 | } 36 | 37 | private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { 38 | switch ((sender as ComboBox).SelectedIndex) { 39 | case 0: 40 | _notify = new Action(() => _manager.Notify("Hello", "Toast")); 41 | break; 42 | case 1: 43 | _notify = new Action(() => _manager.Notify("Hello", "Toast", new ToastCommands { Content = "OK", Argument = "OKarg" }, new ToastCommands { Content = "NO", Argument = "NOarg" })); 44 | break; 45 | case 2: 46 | _notify = new Action(() => _manager.Notify("Hello", "Toast", new ToastCommands[] { new ToastCommands { Content = "Input", Argument = "input" } }, new ToastCommands[] { new ToastCommands { Content = "Reply", Argument = "btn" } })); 47 | break; 48 | } 49 | } 50 | 51 | private void Button_Click_1(object sender, RoutedEventArgs e) { 52 | 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /CoreTets/ToastCore.cs: -------------------------------------------------------------------------------- 1 | ///------------------------------------------------------------------------------ 2 | /// @ Y_Theta 3 | ///------------------------------------------------------------------------------ 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | 9 | using ToastCore.Notification; 10 | 11 | namespace CoreTest { 12 | 13 | [ClassInterface(ClassInterfaceType.None)] 14 | [ComSourceInterfaces(typeof(INotificationActivationCallback))] 15 | [Guid("F6EDC682-FB72-4DE0-A071-C12D0CBF2A48"), ComVisible(true)] 16 | public class ToastManagerCore : NotificationService { 17 | #region Properties 18 | #endregion 19 | 20 | #region Methods 21 | public override void OnActivated(string arguments, NotificationUserInput userInput, string appUserModelId) { 22 | base.OnActivated(arguments, userInput, appUserModelId); 23 | } 24 | #endregion 25 | 26 | #region Constructors 27 | #endregion 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ray 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ToastCore/Notification/ANotificationActivator.cs: -------------------------------------------------------------------------------- 1 | ///------------------------------------------------------------------------------ 2 | /// @ Y_Theta 3 | ///------------------------------------------------------------------------------ 4 | using System; 5 | using System.Runtime.InteropServices; 6 | 7 | namespace ToastCore.Notification { 8 | public abstract class NotificationActivator : INotificationActivationCallback { 9 | 10 | #region Methods 11 | public void Activate(string appUserModelId, string invokedArgs, NOTIFICATION_USER_INPUT_DATA[] data, uint dataCount) { 12 | OnActivated(invokedArgs, new NotificationUserInput(data), appUserModelId); 13 | } 14 | 15 | public abstract void OnActivated(string arguments, NotificationUserInput userInput, string appUserModelId); 16 | 17 | #endregion 18 | } 19 | 20 | [StructLayout(LayoutKind.Sequential), Serializable] 21 | public struct NOTIFICATION_USER_INPUT_DATA { 22 | 23 | [MarshalAs(UnmanagedType.LPWStr)] 24 | public string Key; 25 | 26 | [MarshalAs(UnmanagedType.LPWStr)] 27 | public string Value; 28 | } 29 | 30 | [ComImport, Guid("53E31837-6600-4A81-9395-75CFFE746F94"), ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 31 | public interface INotificationActivationCallback { 32 | void Activate( 33 | [In, MarshalAs(UnmanagedType.LPWStr)] string appUserModelId, 34 | [In, MarshalAs(UnmanagedType.LPWStr)] string invokedArgs, 35 | [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] NOTIFICATION_USER_INPUT_DATA[] data, 36 | [In, MarshalAs(UnmanagedType.U4)] uint dataCount 37 | ); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /ToastCore/Notification/DesktopNotificationHistoryCompat.cs: -------------------------------------------------------------------------------- 1 | ///------------------------------------------------------------------------------ 2 | /// @ Y_Theta 3 | ///------------------------------------------------------------------------------ 4 | using System.Collections.Generic; 5 | using Windows.UI.Notifications; 6 | 7 | namespace ToastCore.Notification { 8 | internal class DesktopNotificationHistoryCompat { 9 | #region Properties 10 | private string _aumid; 11 | private ToastNotificationHistory _history; 12 | 13 | /// 14 | /// Do not call this. Instead, call to obtain an instance. 15 | /// 16 | /// 17 | public DesktopNotificationHistoryCompat(string aumid) { 18 | _aumid = aumid; 19 | _history = ToastNotificationManager.History; 20 | } 21 | #endregion 22 | 23 | #region Methods 24 | /// 25 | /// Removes all notifications sent by this app from action center. 26 | /// 27 | public void Clear() { 28 | if (_aumid != null) { 29 | _history.Clear(_aumid); 30 | } 31 | else { 32 | _history.Clear(); 33 | } 34 | } 35 | 36 | /// 37 | /// Gets all notifications sent by this app that are currently still in Action Center. 38 | /// 39 | /// A collection of toasts. 40 | public IReadOnlyList GetHistory() { 41 | return _aumid != null ? _history.GetHistory(_aumid) : _history.GetHistory(); 42 | } 43 | 44 | /// 45 | /// Removes an individual toast, with the specified tag label, from action center. 46 | /// 47 | /// The tag label of the toast notification to be removed. 48 | public void Remove(string tag) { 49 | if (_aumid != null) { 50 | _history.Remove(tag, string.Empty, _aumid); 51 | } 52 | else { 53 | _history.Remove(tag); 54 | } 55 | } 56 | 57 | /// 58 | /// Removes a toast notification from the action using the notification's tag and group labels. 59 | /// 60 | /// The tag label of the toast notification to be removed. 61 | /// The group label of the toast notification to be removed. 62 | public void Remove(string tag, string group) { 63 | if (_aumid != null) { 64 | _history.Remove(tag, group, _aumid); 65 | } 66 | else { 67 | _history.Remove(tag, group); 68 | } 69 | } 70 | 71 | /// 72 | /// Removes a group of toast notifications, identified by the specified group label, from action center. 73 | /// 74 | /// The group label of the toast notifications to be removed. 75 | public void RemoveGroup(string group) { 76 | if (_aumid != null) { 77 | _history.RemoveGroup(group, _aumid); 78 | } 79 | else { 80 | _history.RemoveGroup(group); 81 | } 82 | } 83 | #endregion 84 | 85 | #region Constructors 86 | #endregion 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /ToastCore/Notification/DesktopNotificationManagerCompat.cs: -------------------------------------------------------------------------------- 1 | ///------------------------------------------------------------------------------ 2 | /// @ Y_Theta 3 | ///------------------------------------------------------------------------------ 4 | using Microsoft.Win32; 5 | using System; 6 | using System.Diagnostics; 7 | using System.IO; 8 | using System.Reflection; 9 | using System.Runtime.InteropServices; 10 | using System.Text; 11 | using ToastCore.Util; 12 | using Windows.UI.Notifications; 13 | 14 | namespace ToastCore.Notification { 15 | internal class DesktopNotificationManagerCompat { 16 | #region Properties 17 | public const string TOAST_ACTIVATED_LAUNCH_ARG = "-ToastActivated"; 18 | static readonly Guid TOAST_G = new Guid("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3"); 19 | 20 | private static bool _registeredAumidAndComServer; 21 | private static string _aumid; 22 | private static bool _registeredActivator; 23 | 24 | #endregion 25 | 26 | #region Methods 27 | public static void RegisterAumidAndComServer(string aumid) 28 | where T : NotificationActivator { 29 | if (string.IsNullOrWhiteSpace(aumid)) { 30 | throw new ArgumentException("You must provide an AUMID.", nameof(aumid)); 31 | } 32 | 33 | // If running as Desktop Bridge 34 | if (DesktopBridgeHelpers.IsRunningAsUwp()) { 35 | _aumid = null; 36 | _registeredAumidAndComServer = true; 37 | return; 38 | } 39 | 40 | _aumid = aumid; 41 | 42 | string exename = Assembly.GetExecutingAssembly().GetName().Name; 43 | string shortpath = "\\Microsoft\\Windows\\Start Menu\\Programs\\Ray\\ClipBoard Qr Helper"; 44 | var shortcut = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) 45 | + shortpath + $"\\{_aumid}.lnk"; 46 | if (!File.Exists(shortcut)) 47 | { 48 | Directory.CreateDirectory(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + shortpath); 49 | // 不需要从通知打开程序则不需要这项操作 50 | RegisterComServer(Process.GetCurrentProcess().MainModule.FileName); 51 | CreatShortcut(shortcut); 52 | } 53 | 54 | _registeredAumidAndComServer = true; 55 | } 56 | 57 | /// 58 | /// 使应用程序可以从toast启动 59 | /// 60 | private static void RegisterComServer(String exePath) 61 | where T : NotificationActivator { 62 | // We register the EXE to start up when the notification is activated 63 | string regString = $"SOFTWARE\\Classes\\CLSID\\{{{typeof(T).GUID.ToString()}}}\\LocalServer32"; 64 | RegistryKey localKey; 65 | if (Environment.Is64BitOperatingSystem) 66 | localKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64); 67 | else 68 | localKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry32); 69 | 70 | var key = localKey.CreateSubKey(regString); 71 | 72 | key.SetValue(null, '"' + exePath + '"'); 73 | } 74 | 75 | /// 76 | /// Registers the activator type as a COM server client so that Windows can launch your activator. 77 | /// 78 | public static void RegisterActivator() 79 | where T : NotificationActivator { 80 | // Register type 81 | var regService = new RegistrationServices(); 82 | 83 | regService.RegisterTypeForComClients( 84 | typeof(T), 85 | RegistrationClassContext.LocalServer, 86 | RegistrationConnectionType.MultipleUse); 87 | 88 | _registeredActivator = true; 89 | } 90 | 91 | /// 92 | /// 创建快捷方式 93 | /// 94 | private static void CreatShortcut(string shortcutPath) { 95 | 96 | String exePath = Process.GetCurrentProcess().MainModule.FileName; 97 | IShellLinkW newShortcut = (IShellLinkW)new CShellLink(); 98 | 99 | ErrorHelper.VerifySucceeded(newShortcut.SetPath(exePath)); 100 | 101 | IPropertyStore newShortcutProperties = (IPropertyStore)newShortcut; 102 | 103 | using (PropVariant appId = new PropVariant(_aumid)) { 104 | ErrorHelper.VerifySucceeded(newShortcutProperties.SetValue(new PropertyKey(TOAST_G, 5), appId)); 105 | } 106 | 107 | using (PropVariant toastid = new PropVariant(typeof(T).GUID)) { 108 | ErrorHelper.VerifySucceeded(newShortcutProperties.SetValue(new PropertyKey(TOAST_G, 26), toastid)); 109 | } 110 | ErrorHelper.VerifySucceeded(newShortcutProperties.Commit()); 111 | 112 | // Commit the shortcut to disk 113 | IPersistFile newShortcutSave = (IPersistFile)newShortcut; 114 | 115 | ErrorHelper.VerifySucceeded(newShortcutSave.Save(shortcutPath, true)); 116 | } 117 | 118 | /// 119 | /// 创建通知 120 | /// 121 | public static ToastNotifier CreateToastNotifier() { 122 | EnsureRegistered(); 123 | 124 | if (_aumid != null) { 125 | // Non-Desktop Bridge 126 | return ToastNotificationManager.CreateToastNotifier(_aumid); 127 | } 128 | else { 129 | // Desktop Bridge 130 | return ToastNotificationManager.CreateToastNotifier(); 131 | } 132 | } 133 | 134 | /// 135 | /// Gets the object. You must have called first (and also if you're a classic Win32 app), or this will throw an exception. 136 | /// 137 | public static DesktopNotificationHistoryCompat History { 138 | get { 139 | EnsureRegistered(); 140 | 141 | return new DesktopNotificationHistoryCompat(_aumid); 142 | } 143 | } 144 | 145 | private static void EnsureRegistered() { 146 | // If not registered AUMID yet 147 | if (!_registeredAumidAndComServer) { 148 | // Check if Desktop Bridge 149 | if (DesktopBridgeHelpers.IsRunningAsUwp()) { 150 | // Implicitly registered, all good! 151 | _registeredAumidAndComServer = true; 152 | } 153 | 154 | else { 155 | // Otherwise, incorrect usage 156 | throw new Exception("You must call RegisterAumidAndComServer first."); 157 | } 158 | } 159 | 160 | // If not registered activator yet 161 | if (!_registeredActivator) { 162 | // Incorrect usage 163 | throw new Exception("You must call RegisterActivator first."); 164 | } 165 | } 166 | 167 | /// 168 | /// Gets a boolean representing whether http images can be used within toasts. This is true if running under Desktop Bridge. 169 | /// 170 | public static bool CanUseHttpImages { get { return DesktopBridgeHelpers.IsRunningAsUwp(); } } 171 | 172 | /// 173 | /// Code from https://github.com/qmatteoq/DesktopBridgeHelpers/edit/master/DesktopBridge.Helpers/Helpers.cs 174 | /// 175 | private class DesktopBridgeHelpers { 176 | const long APPMODEL_ERROR_NO_PACKAGE = 15700L; 177 | 178 | [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] 179 | static extern int GetCurrentPackageFullName(ref int packageFullNameLength, StringBuilder packageFullName); 180 | 181 | private static bool? _isRunningAsUwp; 182 | public static bool IsRunningAsUwp() { 183 | if (_isRunningAsUwp == null) { 184 | if (IsWindows7OrLower) { 185 | _isRunningAsUwp = false; 186 | } 187 | else { 188 | int length = 0; 189 | StringBuilder sb = new StringBuilder(0); 190 | int result = GetCurrentPackageFullName(ref length, sb); 191 | 192 | sb = new StringBuilder(length); 193 | result = GetCurrentPackageFullName(ref length, sb); 194 | 195 | _isRunningAsUwp = result != APPMODEL_ERROR_NO_PACKAGE; 196 | } 197 | } 198 | 199 | return _isRunningAsUwp.Value; 200 | } 201 | 202 | private static bool IsWindows7OrLower { 203 | get { 204 | int versionMajor = Environment.OSVersion.Version.Major; 205 | int versionMinor = Environment.OSVersion.Version.Minor; 206 | double version = versionMajor + (double)versionMinor / 10; 207 | return version <= 6.1; 208 | } 209 | } 210 | } 211 | #endregion 212 | 213 | } 214 | } -------------------------------------------------------------------------------- /ToastCore/Notification/NotificationService.cs: -------------------------------------------------------------------------------- 1 | ///------------------------------------------------------------------------------ 2 | /// @ Y_Theta 3 | ///------------------------------------------------------------------------------ 4 | using System.Collections.Generic; 5 | using XML = System.Xml; 6 | using Windows.Data.Xml.Dom; 7 | using Windows.UI.Notifications; 8 | 9 | namespace ToastCore.Notification { 10 | 11 | /// 12 | /// Win10通知 13 | /// Toast通知回调 14 | /// 15 | /// app的id 16 | /// 按钮的参数 17 | /// 用户输入 18 | public delegate void ToastAction(string app, string arg, List> kvs); 19 | 20 | /// 21 | /// 继承自NotificationActivator 本来是为了使用OnActivated回调 22 | /// 在继承类上添加 23 | /// [ClassInterface(ClassInterfaceType.None)] 24 | /// [ComSourceInterfaces(typeof(INotificationActivationCallback))] 25 | /// [Guid("7ddba60f-e2f0-4373-8098-0eafb79ba54a"), ComVisible(true)] 26 | /// 换上自己的GUID 27 | /// 28 | public class NotificationService : NotificationActivator { 29 | #region Methods 30 | public void Init(string appid) where T : NotificationActivator { 31 | // Console.WriteLine("Init" + Thread.CurrentThread.ManagedThreadId); 32 | DesktopNotificationManagerCompat.RegisterAumidAndComServer(appid); 33 | DesktopNotificationManagerCompat.RegisterActivator(); 34 | } 35 | 36 | /// 37 | /// 通知响应事件,在使用时接收 38 | /// 39 | public static event ToastAction ToastCallback; 40 | 41 | /// 42 | /// 微软提供的回调,调用者不在当前上下文线程中 43 | /// 44 | public override void OnActivated(string arguments, NotificationUserInput userInput, string appUserModelId) { 45 | List> kvs = new List>(); 46 | if (userInput != null && userInput.Count > 0) 47 | foreach (var key in userInput.Keys) { 48 | kvs.Add(new KeyValuePair(key, userInput[key])); 49 | } 50 | ToastCallback?.Invoke(appUserModelId, arguments, kvs); 51 | } 52 | 53 | /// 54 | /// 发送一条自定义格式通知 55 | /// 56 | public void Notify() { 57 | XmlDocument xml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02); 58 | XML.XmlDocument xxml = new XML.XmlDocument(); 59 | 60 | OnSetNotifyXML(xml.GetXml()); 61 | // ShowToast(xml); 62 | } 63 | 64 | /// 65 | /// 重写此方法以自定义通知xml内容 66 | /// 67 | /// 68 | protected virtual void OnSetNotifyXML(string xml) { } 69 | 70 | /// 71 | /// 发送一条通知 (标题/文本) 72 | /// 73 | /// 标题 74 | /// 文本 75 | public void Notify(string title, string content) { 76 | XmlDocument xml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02); 77 | AddTitle(xml, title, content); 78 | ShowToast(xml); 79 | } 80 | 81 | /// 82 | /// 发送一条通知 (标题/文本/自定义命令) 83 | /// 84 | /// 标题 85 | /// 文本 86 | /// 自定义命令组 87 | public void Notify(string title, string content, params ToastCommands[] commands) { 88 | XmlDocument xml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02); 89 | AddTitle(xml, title, content); 90 | AddCommands(xml, commands); 91 | ShowToast(xml); 92 | } 93 | 94 | /// 95 | /// 发送一条通知 (自定义图标/标题/文本) 96 | /// 97 | /// 自定义图标路径 98 | /// 标题 99 | /// 文本 100 | public void Notify(string picuri, string title, string content) { 101 | XmlDocument xml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02); 102 | AddTitle(xml, title, content); 103 | AddBigLogo(xml, picuri); 104 | ShowToast(xml); 105 | } 106 | 107 | /// 108 | /// 发送一条通知 (自定义图标/标题/文本/自定义命令) 109 | /// 110 | /// 自定义图标路径 111 | /// 标题 112 | /// 文本 113 | /// 自定义命令组 114 | public void Notify(string picuri, string title, string content, params ToastCommands[] commands) { 115 | XmlDocument xml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02); 116 | AddTitle(xml, title, content); 117 | AddBigLogo(xml, picuri); 118 | AddCommands(xml, commands); 119 | ShowToast(xml); 120 | } 121 | 122 | public void Notify(string title, string content, ToastCommands[] paras, ToastCommands[] commands) { 123 | XmlDocument xml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02); 124 | AddTitle(xml, title, content); 125 | AddInput(xml, paras); 126 | AddCommands(xml, commands); 127 | ShowToast(xml); 128 | } 129 | 130 | /// 131 | /// 发送通知 132 | /// 133 | protected static void ShowToast(XmlDocument xml) { 134 | ToastNotification toast = new ToastNotification(xml); 135 | DesktopNotificationManagerCompat.CreateToastNotifier().Show(toast); 136 | } 137 | 138 | /// 139 | /// 添加标题和内容描述 140 | /// 141 | protected static void AddTitle(XmlDocument xml, string title, string content) { 142 | XmlNodeList lines = xml.GetElementsByTagName("text"); 143 | lines[0].AppendChild(xml.CreateTextNode(title)); 144 | lines[1].AppendChild(xml.CreateTextNode(content)); 145 | } 146 | 147 | /// 148 | /// 为当前通知添加交互操作 149 | /// 150 | protected static void AddCommands(XmlDocument xml, params ToastCommands[] commands) { 151 | XmlElement actions = GetAction(xml); 152 | 153 | foreach (var command in commands) { 154 | XmlElement action = xml.CreateElement("action"); 155 | action.SetAttribute("activationType", "foreground"); 156 | action.SetAttribute("arguments", command.Argument); 157 | action.SetAttribute("content", command.Content); 158 | actions.AppendChild(action); 159 | } 160 | } 161 | 162 | /// 163 | /// 添加输入框 164 | /// 165 | protected static void AddInput(XmlDocument xml, params ToastCommands[] paras) { 166 | XmlElement actions = GetAction(xml); 167 | 168 | foreach (var para in paras) { 169 | XmlElement input = xml.CreateElement("input"); 170 | input.SetAttribute("type", "text"); 171 | input.SetAttribute("id", para.Argument); 172 | input.SetAttribute("placeHolderContent", para.Content); 173 | actions?.AppendChild(input); 174 | } 175 | } 176 | 177 | /// 178 | /// 为通知添加大标签 179 | /// 180 | protected static void AddBigLogo(XmlDocument xml, string logopath) { 181 | //获得binding组 182 | XmlElement binding = (XmlElement)xml.GetElementsByTagName("binding")[0]; 183 | binding.SetAttribute("template", "ToastGeneric"); 184 | //创建大图标 185 | XmlElement image = xml.CreateElement("image"); 186 | image.SetAttribute("placement", "appLogoOverride"); 187 | image.SetAttribute("src", logopath); 188 | binding.AppendChild(image); 189 | } 190 | 191 | /// 192 | /// 获取action组 193 | /// 194 | private static XmlElement GetAction(XmlDocument xml) { 195 | XmlElement actions = null; 196 | if (xml.GetElementsByTagName("actions").Count != 0) 197 | actions = (XmlElement)xml.GetElementsByTagName("actions")[0]; 198 | else { 199 | actions = xml.CreateElement("actions"); 200 | ((XmlElement)xml.GetElementsByTagName("toast")[0]).AppendChild(actions); 201 | } 202 | return actions; 203 | } 204 | 205 | /// 206 | /// 清除对应App的所有通知 207 | /// 208 | /// app标识 209 | public void ClearHistory(string appid) { 210 | new DesktopNotificationHistoryCompat(appid).Clear(); 211 | } 212 | #endregion 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /ToastCore/Notification/NotificationUserInput.cs: -------------------------------------------------------------------------------- 1 | ///------------------------------------------------------------------------------ 2 | /// @ Y_Theta 3 | ///------------------------------------------------------------------------------ 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | namespace ToastCore.Notification { 9 | public class NotificationUserInput : IReadOnlyDictionary { 10 | #region Properties 11 | private NOTIFICATION_USER_INPUT_DATA[] _data; 12 | 13 | internal NotificationUserInput(NOTIFICATION_USER_INPUT_DATA[] data) { 14 | _data = data; 15 | } 16 | 17 | public string this[string key] => _data.First(i => i.Key == key).Value; 18 | 19 | public IEnumerable Keys => _data.Select(i => i.Key); 20 | 21 | public IEnumerable Values => _data.Select(i => i.Value); 22 | 23 | public int Count => _data is null ? 0 : _data.Length; 24 | #endregion 25 | 26 | #region Methods 27 | public bool ContainsKey(string key) { 28 | return _data.Any(i => i.Key == key); 29 | } 30 | 31 | public IEnumerator> GetEnumerator() { 32 | return _data.Select(i => new KeyValuePair(i.Key, i.Value)).GetEnumerator(); 33 | } 34 | 35 | public bool TryGetValue(string key, out string value) { 36 | foreach (var item in _data) { 37 | if (item.Key == key) { 38 | value = item.Value; 39 | return true; 40 | } 41 | } 42 | 43 | value = null; 44 | return false; 45 | } 46 | 47 | IEnumerator IEnumerable.GetEnumerator() { 48 | return GetEnumerator(); 49 | } 50 | #endregion 51 | 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /ToastCore/Notification/ToastCommands.cs: -------------------------------------------------------------------------------- 1 | ///------------------------------------------------------------------------------ 2 | /// @ Y_Theta 3 | ///------------------------------------------------------------------------------ 4 | 5 | 6 | namespace ToastCore.Notification { 7 | 8 | /// 9 | /// Toast通知的按钮命令 10 | /// 11 | public struct ToastCommands { 12 | #region Properties 13 | public string Argument; 14 | 15 | public string Content; 16 | #endregion 17 | 18 | #region Constructors 19 | public ToastCommands(string arg, string content) { 20 | Argument = arg; 21 | Content = content; 22 | } 23 | #endregion 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /ToastCore/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("ToastCore")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ToastCore")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("18f57aa0-e5ed-4569-a074-a0c622d3d52b")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 33 | //通过使用 "*",如下所示: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /ToastCore/ToastCore.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {18F57AA0-E5ED-4569-A074-A0C622D3D52B} 8 | Library 9 | Properties 10 | ToastCore 11 | ToastCore 12 | v4.5 13 | 512 14 | true 15 | 16 | 17 | 18 | 19 | 20 | true 21 | full 22 | false 23 | ..\Test\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | MinimumRecommendedRules.ruleset 28 | 29 | 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | ..\..\..\..\..\..\Windows Kits\10\References\10.0.18362.0\Windows.Foundation.FoundationContract\3.0.0.0\Windows.Foundation.FoundationContract.winmd 48 | 49 | 50 | ..\..\..\..\..\..\Windows Kits\10\References\10.0.18362.0\Windows.Foundation.UniversalApiContract\8.0.0.0\Windows.Foundation.UniversalApiContract.winmd 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /ToastCore/Util/ShellHelper.cs: -------------------------------------------------------------------------------- 1 | ///------------------------------------------------------------------------------ 2 | /// @ Y_Theta 3 | ///------------------------------------------------------------------------------ 4 | using System; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | 8 | namespace ToastCore.Util { 9 | 10 | [StructLayout(LayoutKind.Sequential, Pack = 4)] 11 | public struct PropertyKey { 12 | #region Private Fields 13 | 14 | private Guid formatId; 15 | private Int32 propertyId; 16 | #endregion 17 | 18 | #region Public Construction 19 | 20 | public PropertyKey(Guid formatId, Int32 propertyId) { 21 | this.formatId = formatId; 22 | this.propertyId = propertyId; 23 | } 24 | #endregion 25 | } 26 | 27 | internal enum STGM : long { 28 | STGM_READ = 0x00000000L, 29 | STGM_WRITE = 0x00000001L, 30 | STGM_READWRITE = 0x00000002L, 31 | STGM_SHARE_DENY_NONE = 0x00000040L, 32 | STGM_SHARE_DENY_READ = 0x00000030L, 33 | STGM_SHARE_DENY_WRITE = 0x00000020L, 34 | STGM_SHARE_EXCLUSIVE = 0x00000010L, 35 | STGM_PRIORITY = 0x00040000L, 36 | STGM_CREATE = 0x00001000L, 37 | STGM_CONVERT = 0x00020000L, 38 | STGM_FAILIFTHERE = 0x00000000L, 39 | STGM_DIRECT = 0x00000000L, 40 | STGM_TRANSACTED = 0x00010000L, 41 | STGM_NOSCRATCH = 0x00100000L, 42 | STGM_NOSNAPSHOT = 0x00200000L, 43 | STGM_SIMPLE = 0x08000000L, 44 | STGM_DIRECT_SWMR = 0x00400000L, 45 | STGM_DELETEONRELEASE = 0x04000000L, 46 | } 47 | 48 | [ComImport, 49 | Guid("000214F9-0000-0000-C000-000000000046"), 50 | InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 51 | internal interface IShellLinkW { 52 | UInt32 GetPath( 53 | [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, 54 | int cchMaxPath, 55 | //ref _WIN32_FIND_DATAW pfd, 56 | IntPtr pfd, 57 | uint fFlags); 58 | UInt32 GetIDList(out IntPtr ppidl); 59 | UInt32 SetIDList(IntPtr pidl); 60 | UInt32 GetDescription( 61 | [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, 62 | int cchMaxName); 63 | UInt32 SetDescription( 64 | [MarshalAs(UnmanagedType.LPWStr)] string pszName); 65 | UInt32 GetWorkingDirectory( 66 | [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, 67 | int cchMaxPath 68 | ); 69 | UInt32 SetWorkingDirectory( 70 | [MarshalAs(UnmanagedType.LPWStr)] string pszDir); 71 | UInt32 GetArguments( 72 | [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, 73 | int cchMaxPath); 74 | UInt32 SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); 75 | UInt32 GetHotKey(out short wHotKey); 76 | UInt32 SetHotKey(short wHotKey); 77 | UInt32 GetShowCmd(out uint iShowCmd); 78 | UInt32 SetShowCmd(uint iShowCmd); 79 | UInt32 GetIconLocation( 80 | [Out(), MarshalAs(UnmanagedType.LPWStr)] out StringBuilder pszIconPath, 81 | int cchIconPath, 82 | out int iIcon); 83 | UInt32 SetIconLocation( 84 | [MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, 85 | int iIcon); 86 | UInt32 SetRelativePath( 87 | [MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, 88 | uint dwReserved); 89 | UInt32 Resolve(IntPtr hwnd, uint fFlags); 90 | UInt32 SetPath( 91 | [MarshalAs(UnmanagedType.LPWStr)] string pszFile); 92 | } 93 | 94 | [ComImport] 95 | [Guid("0000010b-0000-0000-C000-000000000046")] 96 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 97 | internal interface IPersistFile { 98 | UInt32 GetCurFile( 99 | [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile 100 | ); 101 | UInt32 IsDirty(); 102 | UInt32 Load( 103 | [MarshalAs(UnmanagedType.LPWStr)] string pszFileName, 104 | [MarshalAs(UnmanagedType.U4)] STGM dwMode); 105 | UInt32 Save( 106 | [MarshalAs(UnmanagedType.LPWStr)] string pszFileName, 107 | bool fRemember); 108 | UInt32 SaveCompleted( 109 | [MarshalAs(UnmanagedType.LPWStr)] string pszFileName); 110 | } 111 | 112 | [ComImport] 113 | [Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")] 114 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 115 | interface IPropertyStore { 116 | UInt32 GetCount([Out] out uint propertyCount); 117 | UInt32 GetAt([In] uint propertyIndex, out PropertyKey key); 118 | UInt32 GetValue([In] ref PropertyKey key, [Out] PropVariant pv); 119 | UInt32 SetValue([In] ref PropertyKey key, [In] PropVariant pv); 120 | UInt32 Commit(); 121 | } 122 | 123 | /// 124 | /// 承载IShellLinkW 125 | /// 126 | [ComImport, 127 | Guid("00021401-0000-0000-C000-000000000046"), 128 | ClassInterface(ClassInterfaceType.None)] 129 | internal class CShellLink { } 130 | 131 | public static class ErrorHelper { 132 | public static void VerifySucceeded(UInt32 hresult) { 133 | if (hresult > 1) { 134 | throw new Exception("Failed with HRESULT: " + hresult.ToString("X")); 135 | } 136 | } 137 | } 138 | 139 | /// 140 | /// 对c++中一种结构的封装 141 | /// 142 | [StructLayout(LayoutKind.Sequential)] 143 | public sealed class PropVariant : IDisposable { 144 | #region Fields 145 | 146 | ushort _valueType; 147 | ushort wReserved1; 148 | ushort wReserved2; 149 | ushort wReserved3; 150 | IntPtr _ptr; 151 | 152 | #endregion 153 | 154 | #region Constructors 155 | public PropVariant(string value) { 156 | if (value == null) { 157 | throw new ArgumentException(); 158 | } 159 | _valueType = (ushort)VarEnum.VT_LPWSTR; 160 | _ptr = Marshal.StringToCoTaskMemUni(value); 161 | } 162 | 163 | public PropVariant(Guid guid) { 164 | if (guid == null) { 165 | throw new ArgumentException(); 166 | } 167 | byte[] bytes = guid.ToByteArray(); 168 | _valueType = (ushort)VarEnum.VT_CLSID; 169 | _ptr = Marshal.AllocCoTaskMem(bytes.Length); 170 | Marshal.Copy(bytes, 0, _ptr, bytes.Length); 171 | } 172 | 173 | #endregion 174 | 175 | #region IDisposable Members 176 | [DllImport("Ole32.dll")] // returns hresult 177 | internal extern static void PropVariantClear([In, Out] PropVariant pvar); 178 | 179 | public void Dispose() { 180 | PropVariantClear(this); 181 | GC.SuppressFinalize(this); 182 | } 183 | 184 | ~PropVariant() { 185 | Dispose(); 186 | } 187 | 188 | #endregion 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /ToastCore[netcore]/Notification/ANotificationActivator.cs: -------------------------------------------------------------------------------- 1 | ///------------------------------------------------------------------------------ 2 | /// @ Y_Theta 3 | ///------------------------------------------------------------------------------ 4 | using System; 5 | using System.Runtime.InteropServices; 6 | 7 | namespace ToastCore.Notification { 8 | public abstract class NotificationActivator : INotificationActivationCallback { 9 | 10 | #region Methods 11 | public void Activate(string appUserModelId, string invokedArgs, NOTIFICATION_USER_INPUT_DATA[] data, uint dataCount) { 12 | OnActivated(invokedArgs, new NotificationUserInput(data), appUserModelId); 13 | } 14 | 15 | public abstract void OnActivated(string arguments, NotificationUserInput userInput, string appUserModelId); 16 | 17 | #endregion 18 | } 19 | 20 | [StructLayout(LayoutKind.Sequential), Serializable] 21 | public struct NOTIFICATION_USER_INPUT_DATA { 22 | 23 | [MarshalAs(UnmanagedType.LPWStr)] 24 | public string Key; 25 | 26 | [MarshalAs(UnmanagedType.LPWStr)] 27 | public string Value; 28 | } 29 | 30 | [ComImport, Guid("53E31837-6600-4A81-9395-75CFFE746F94"), ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 31 | public interface INotificationActivationCallback { 32 | void Activate( 33 | [In, MarshalAs(UnmanagedType.LPWStr)] string appUserModelId, 34 | [In, MarshalAs(UnmanagedType.LPWStr)] string invokedArgs, 35 | [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] NOTIFICATION_USER_INPUT_DATA[] data, 36 | [In, MarshalAs(UnmanagedType.U4)] uint dataCount 37 | ); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /ToastCore[netcore]/Notification/DesktopNotificationHistoryCompat.cs: -------------------------------------------------------------------------------- 1 | ///------------------------------------------------------------------------------ 2 | /// @ Y_Theta 3 | ///------------------------------------------------------------------------------ 4 | using System.Collections.Generic; 5 | using Windows.UI.Notifications; 6 | 7 | namespace ToastCore.Notification { 8 | internal class DesktopNotificationHistoryCompat { 9 | #region Properties 10 | private string _aumid; 11 | private ToastNotificationHistory _history; 12 | 13 | /// 14 | /// Do not call this. Instead, call to obtain an instance. 15 | /// 16 | /// 17 | public DesktopNotificationHistoryCompat(string aumid) { 18 | _aumid = aumid; 19 | _history = ToastNotificationManager.History; 20 | } 21 | #endregion 22 | 23 | #region Methods 24 | /// 25 | /// Removes all notifications sent by this app from action center. 26 | /// 27 | public void Clear() { 28 | if (_aumid != null) { 29 | _history.Clear(_aumid); 30 | } 31 | else { 32 | _history.Clear(); 33 | } 34 | } 35 | 36 | /// 37 | /// Gets all notifications sent by this app that are currently still in Action Center. 38 | /// 39 | /// A collection of toasts. 40 | public IReadOnlyList GetHistory() { 41 | return _aumid != null ? _history.GetHistory(_aumid) : _history.GetHistory(); 42 | } 43 | 44 | /// 45 | /// Removes an individual toast, with the specified tag label, from action center. 46 | /// 47 | /// The tag label of the toast notification to be removed. 48 | public void Remove(string tag) { 49 | if (_aumid != null) { 50 | _history.Remove(tag, string.Empty, _aumid); 51 | } 52 | else { 53 | _history.Remove(tag); 54 | } 55 | } 56 | 57 | /// 58 | /// Removes a toast notification from the action using the notification's tag and group labels. 59 | /// 60 | /// The tag label of the toast notification to be removed. 61 | /// The group label of the toast notification to be removed. 62 | public void Remove(string tag, string group) { 63 | if (_aumid != null) { 64 | _history.Remove(tag, group, _aumid); 65 | } 66 | else { 67 | _history.Remove(tag, group); 68 | } 69 | } 70 | 71 | /// 72 | /// Removes a group of toast notifications, identified by the specified group label, from action center. 73 | /// 74 | /// The group label of the toast notifications to be removed. 75 | public void RemoveGroup(string group) { 76 | if (_aumid != null) { 77 | _history.RemoveGroup(group, _aumid); 78 | } 79 | else { 80 | _history.RemoveGroup(group); 81 | } 82 | } 83 | #endregion 84 | 85 | #region Constructors 86 | #endregion 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /ToastCore[netcore]/Notification/DesktopNotificationManagerCompat.cs: -------------------------------------------------------------------------------- 1 | ///------------------------------------------------------------------------------ 2 | /// @ Y_Theta 3 | ///------------------------------------------------------------------------------ 4 | using Microsoft.Win32; 5 | 6 | using System; 7 | using System.Diagnostics; 8 | using System.IO; 9 | using System.Reflection; 10 | using System.Runtime.CompilerServices; 11 | using System.Runtime.InteropServices; 12 | using System.Runtime.InteropServices.ComTypes; 13 | using System.Text; 14 | 15 | using Windows.UI.Notifications; 16 | 17 | namespace ToastCore.Notification { 18 | internal class DesktopNotificationManagerCompat { 19 | #region Properties 20 | public const string TOAST_ACTIVATED_LAUNCH_ARG = "-ToastActivated"; 21 | static readonly Guid TOAST_G = new Guid("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3"); 22 | 23 | private static bool _registeredAumidAndComServer; 24 | private static string _aumid; 25 | private static bool _registeredActivator; 26 | 27 | 28 | #if NETCOREAPP3_1 29 | [DllImport("Ole32.dll")] 30 | static extern int CoRegisterClassObject( 31 | [MarshalAs(UnmanagedType.LPStruct)] Guid rclsid, 32 | [MarshalAs(UnmanagedType.IUnknown)] object pUnk, 33 | uint dwClsContext, 34 | uint flags, 35 | out uint lpdwRegister); 36 | #endif 37 | #endregion 38 | 39 | #region Methods 40 | public static void RegisterAumidAndComServer(string aumid) 41 | where T : NotificationActivator { 42 | if (string.IsNullOrWhiteSpace(aumid)) { 43 | throw new ArgumentException("You must provide an AUMID.", nameof(aumid)); 44 | } 45 | 46 | // If running as Desktop Bridge 47 | if (DesktopBridgeHelpers.IsRunningAsUwp()) { 48 | _aumid = null; 49 | _registeredAumidAndComServer = true; 50 | return; 51 | } 52 | 53 | _aumid = aumid; 54 | 55 | string exename = Assembly.GetExecutingAssembly().GetName().Name; 56 | string shortpath = "\\Microsoft\\Windows\\Start Menu\\Programs\\Ray\\ClipBoard Qr Helper"; 57 | var shortcut = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) 58 | + shortpath + $"\\{_aumid}.lnk"; 59 | if (!File.Exists(shortcut)) { 60 | Directory.CreateDirectory(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + shortpath); 61 | // 不需要从通知打开程序则不需要这项操作 62 | // RegisterComServer(exePath); 63 | CreatShortcut(shortcut); 64 | } 65 | 66 | _registeredAumidAndComServer = true; 67 | } 68 | 69 | /// 70 | /// 使应用程序可以从toast启动 71 | /// 72 | private static void RegisterComServer(String exePath) 73 | where T : NotificationActivator { 74 | // We register the EXE to start up when the notification is activated 75 | string regString = $"SOFTWARE\\Classes\\CLSID\\{{{typeof(T).GUID.ToString()}}}\\LocalServer32"; 76 | RegistryKey localKey; 77 | if (Environment.Is64BitOperatingSystem) 78 | localKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64); 79 | else 80 | localKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry32); 81 | 82 | var key = localKey.CreateSubKey(regString); 83 | 84 | key.SetValue(null, '"' + exePath + '"'); 85 | } 86 | 87 | /// 88 | /// Registers the activator type as a COM server client so that Windows can launch your activator. 89 | /// 90 | 91 | 92 | public static void RegisterActivator() 93 | where T : NotificationActivator { 94 | 95 | #if NET45 96 | var regService = new RegistrationServices(); 97 | 98 | regService.RegisterTypeForComClients( 99 | typeof(T), 100 | RegistrationClassContext.LocalServer, 101 | RegistrationConnectionType.MultipleUse); 102 | _registeredActivator = true; 103 | 104 | #elif NETCOREAPP3_1 105 | 106 | //Ole32.GetRunningObjectTable(0, out IRunningObjectTable rot); 107 | //Guid id = typeof(T).GUID; 108 | //CreateClassMoniker(ref id,out IMoniker moniker); 109 | //rot.Register(1, instance, moniker); 110 | 111 | ClassFactory factory = new ClassFactory(typeof(T)); 112 | IntPtr iunknow = Marshal.GetIUnknownForObject(factory); 113 | CoRegisterClassObject( 114 | typeof(T).GUID, 115 | factory, 116 | 4, 117 | 0, 118 | out uint id); 119 | 120 | 121 | _registeredActivator = true; 122 | 123 | #endif 124 | } 125 | 126 | /// 127 | /// 创建快捷方式 128 | /// 129 | private static void CreatShortcut(string shortcutPath) { 130 | 131 | String exePath = Process.GetCurrentProcess().MainModule.FileName; 132 | IShellLinkW newShortcut = (IShellLinkW)new CShellLink(); 133 | 134 | ErrorHelper.VerifySucceeded(newShortcut.SetPath(exePath)); 135 | 136 | IPropertyStore newShortcutProperties = (IPropertyStore)newShortcut; 137 | 138 | using (PropVariant appId = new PropVariant(_aumid)) { 139 | ErrorHelper.VerifySucceeded(newShortcutProperties.SetValue(new PropertyKey(TOAST_G, 5), appId)); 140 | } 141 | 142 | using (PropVariant toastid = new PropVariant(typeof(T).GUID)) { 143 | ErrorHelper.VerifySucceeded(newShortcutProperties.SetValue(new PropertyKey(TOAST_G, 26), toastid)); 144 | } 145 | ErrorHelper.VerifySucceeded(newShortcutProperties.Commit()); 146 | 147 | // Commit the shortcut to disk 148 | IPersistFile newShortcutSave = (IPersistFile)newShortcut; 149 | 150 | ErrorHelper.VerifySucceeded(newShortcutSave.Save(shortcutPath, true)); 151 | } 152 | 153 | /// 154 | /// 创建通知 155 | /// 156 | public static ToastNotifier CreateToastNotifier() { 157 | EnsureRegistered(); 158 | 159 | if (_aumid != null) { 160 | // Non-Desktop Bridge 161 | return ToastNotificationManager.CreateToastNotifier(_aumid); 162 | } else { 163 | // Desktop Bridge 164 | return ToastNotificationManager.CreateToastNotifier(); 165 | } 166 | } 167 | 168 | /// 169 | /// Gets the object. You must have called first (and also if you're a classic Win32 app), or this will throw an exception. 170 | /// 171 | public static DesktopNotificationHistoryCompat History { 172 | get { 173 | EnsureRegistered(); 174 | 175 | return new DesktopNotificationHistoryCompat(_aumid); 176 | } 177 | } 178 | 179 | private static void EnsureRegistered() { 180 | // If not registered AUMID yet 181 | if (!_registeredAumidAndComServer) { 182 | // Check if Desktop Bridge 183 | if (DesktopBridgeHelpers.IsRunningAsUwp()) { 184 | // Implicitly registered, all good! 185 | _registeredAumidAndComServer = true; 186 | } else { 187 | // Otherwise, incorrect usage 188 | throw new Exception("You must call RegisterAumidAndComServer first."); 189 | } 190 | } 191 | 192 | // If not registered activator yet 193 | if (!_registeredActivator) { 194 | // Incorrect usage 195 | throw new Exception("You must call RegisterActivator first."); 196 | } 197 | } 198 | 199 | /// 200 | /// Gets a boolean representing whether http images can be used within toasts. This is true if running under Desktop Bridge. 201 | /// 202 | public static bool CanUseHttpImages { get { return DesktopBridgeHelpers.IsRunningAsUwp(); } } 203 | 204 | /// 205 | /// Code from https://github.com/qmatteoq/DesktopBridgeHelpers/edit/master/DesktopBridge.Helpers/Helpers.cs 206 | /// 207 | private class DesktopBridgeHelpers { 208 | const long APPMODEL_ERROR_NO_PACKAGE = 15700L; 209 | 210 | [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] 211 | static extern int GetCurrentPackageFullName(ref int packageFullNameLength, StringBuilder packageFullName); 212 | 213 | private static bool? _isRunningAsUwp; 214 | public static bool IsRunningAsUwp() { 215 | if (_isRunningAsUwp == null) { 216 | if (IsWindows7OrLower) { 217 | _isRunningAsUwp = false; 218 | } else { 219 | int length = 0; 220 | StringBuilder sb = new StringBuilder(0); 221 | int result = GetCurrentPackageFullName(ref length, sb); 222 | 223 | sb = new StringBuilder(length); 224 | result = GetCurrentPackageFullName(ref length, sb); 225 | 226 | _isRunningAsUwp = result != APPMODEL_ERROR_NO_PACKAGE; 227 | } 228 | } 229 | 230 | return _isRunningAsUwp.Value; 231 | } 232 | 233 | private static bool IsWindows7OrLower { 234 | get { 235 | int versionMajor = Environment.OSVersion.Version.Major; 236 | int versionMinor = Environment.OSVersion.Version.Minor; 237 | double version = versionMajor + (double)versionMinor / 10; 238 | return version <= 6.1; 239 | } 240 | } 241 | } 242 | #endregion 243 | 244 | } 245 | } -------------------------------------------------------------------------------- /ToastCore[netcore]/Notification/NotificationService.cs: -------------------------------------------------------------------------------- 1 | ///------------------------------------------------------------------------------ 2 | /// @ Y_Theta 3 | ///------------------------------------------------------------------------------ 4 | using System.Collections.Generic; 5 | using XML = System.Xml; 6 | using Windows.Data.Xml.Dom; 7 | using Windows.UI.Notifications; 8 | 9 | namespace ToastCore.Notification { 10 | 11 | /// 12 | /// Win10通知 13 | /// Toast通知回调 14 | /// 15 | /// app的id 16 | /// 按钮的参数 17 | /// 用户输入 18 | public delegate void ToastAction(string app, string arg, List> kvs); 19 | 20 | /// 21 | /// 继承自NotificationActivator 本来是为了使用OnActivated回调 22 | /// 在继承类上添加 23 | /// [ClassInterface(ClassInterfaceType.None)] 24 | /// [ComSourceInterfaces(typeof(INotificationActivationCallback))] 25 | /// [Guid("7ddba60f-e2f0-4373-8098-0eafb79ba54a"), ComVisible(true)] 26 | /// 换上自己的GUID 27 | /// 28 | public class NotificationService : NotificationActivator { 29 | #region Methods 30 | public void Init(string appid) where T : NotificationActivator { 31 | // Console.WriteLine("Init" + Thread.CurrentThread.ManagedThreadId); 32 | DesktopNotificationManagerCompat.RegisterAumidAndComServer(appid); 33 | DesktopNotificationManagerCompat.RegisterActivator(); 34 | } 35 | 36 | /// 37 | /// 通知响应事件,在使用时接收 38 | /// 39 | public static event ToastAction ToastCallback; 40 | 41 | /// 42 | /// 微软提供的回调,调用者不在当前上下文线程中 43 | /// 44 | public override void OnActivated(string arguments, NotificationUserInput userInput, string appUserModelId) { 45 | List> kvs = new List>(); 46 | if (userInput != null && userInput.Count > 0) 47 | foreach (var key in userInput.Keys) { 48 | kvs.Add(new KeyValuePair(key, userInput[key])); 49 | } 50 | ToastCallback?.Invoke(appUserModelId, arguments, kvs); 51 | } 52 | 53 | /// 54 | /// 发送一条自定义格式通知 55 | /// 56 | public void Notify() { 57 | XmlDocument xml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02); 58 | XML.XmlDocument xxml = new XML.XmlDocument(); 59 | 60 | OnSetNotifyXML(xml.GetXml()); 61 | // ShowToast(xml); 62 | } 63 | 64 | /// 65 | /// 重写此方法以自定义通知xml内容 66 | /// 67 | /// 68 | protected virtual void OnSetNotifyXML(string xml) { } 69 | 70 | 71 | 72 | 73 | /// 74 | /// 发送一条通知 (标题/文本) 75 | /// 76 | /// 标题 77 | /// 文本 78 | public void Notify(string title, string content) { 79 | XmlDocument xml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02); 80 | AddTitle(xml, title, content); 81 | ShowToast(xml); 82 | } 83 | 84 | /// 85 | /// 发送一条通知 (标题/文本/自定义命令) 86 | /// 87 | /// 标题 88 | /// 文本 89 | /// 自定义命令组 90 | public void Notify(string title, string content, params ToastCommands[] commands) { 91 | XmlDocument xml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02); 92 | AddTitle(xml, title, content); 93 | AddCommands(xml, commands); 94 | ShowToast(xml); 95 | } 96 | 97 | /// 98 | /// 发送一条通知 (自定义图标/标题/文本) 99 | /// 100 | /// 自定义图标路径 101 | /// 标题 102 | /// 文本 103 | public void Notify(string picuri, string title, string content) { 104 | XmlDocument xml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02); 105 | AddTitle(xml, title, content); 106 | AddBigLogo(xml, picuri); 107 | ShowToast(xml); 108 | } 109 | 110 | /// 111 | /// 发送一条通知 (自定义图标/标题/文本/自定义命令) 112 | /// 113 | /// 自定义图标路径 114 | /// 标题 115 | /// 文本 116 | /// 自定义命令组 117 | public void Notify(string picuri, string title, string content, params ToastCommands[] commands) { 118 | XmlDocument xml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02); 119 | AddTitle(xml, title, content); 120 | AddBigLogo(xml, picuri); 121 | AddCommands(xml, commands); 122 | ShowToast(xml); 123 | } 124 | 125 | public void Notify(string title, string content, ToastCommands[] paras, ToastCommands[] commands) { 126 | XmlDocument xml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02); 127 | AddTitle(xml, title, content); 128 | AddCommands(xml, paras); 129 | AddCommands(xml, commands); 130 | ShowToast(xml); 131 | } 132 | 133 | /// 134 | /// 发送通知 135 | /// 136 | protected static void ShowToast(XmlDocument xml) { 137 | ToastNotification toast = new ToastNotification(xml); 138 | DesktopNotificationManagerCompat.CreateToastNotifier().Show(toast); 139 | } 140 | 141 | /// 142 | /// 添加标题和内容描述 143 | /// 144 | protected static void AddTitle(XmlDocument xml, string title, string content) { 145 | XmlNodeList lines = xml.GetElementsByTagName("text"); 146 | lines[0].AppendChild(xml.CreateTextNode(title)); 147 | lines[1].AppendChild(xml.CreateTextNode(content)); 148 | } 149 | 150 | /// 151 | /// 为当前通知添加交互操作 152 | /// 153 | protected static void AddCommands(XmlDocument xml, params ToastCommands[] commands) { 154 | XmlElement actions = GetAction(xml); 155 | 156 | foreach (var command in commands) { 157 | XmlElement action = xml.CreateElement("action"); 158 | action.SetAttribute("activationType", "foreground"); 159 | action.SetAttribute("arguments", command.Argument); 160 | action.SetAttribute("content", command.Content); 161 | actions.AppendChild(action); 162 | } 163 | } 164 | 165 | /// 166 | /// 添加输入框 167 | /// 168 | protected static void AddInput(XmlDocument xml, params ToastCommands[] paras) { 169 | XmlElement actions = GetAction(xml); 170 | 171 | foreach (var para in paras) { 172 | XmlElement input = xml.CreateElement("input"); 173 | input.SetAttribute("type", "text"); 174 | input.SetAttribute("id", para.Argument); 175 | input.SetAttribute("placeHolderContent", para.Content); 176 | actions?.AppendChild(input); 177 | } 178 | } 179 | 180 | /// 181 | /// 为通知添加大标签 182 | /// 183 | protected static void AddBigLogo(XmlDocument xml, string logopath) { 184 | //获得binding组 185 | XmlElement binding = (XmlElement)xml.GetElementsByTagName("binding")[0]; 186 | binding.SetAttribute("template", "ToastGeneric"); 187 | //创建大图标 188 | XmlElement image = xml.CreateElement("image"); 189 | image.SetAttribute("placement", "appLogoOverride"); 190 | image.SetAttribute("src", logopath); 191 | binding.AppendChild(image); 192 | } 193 | 194 | /// 195 | /// 获取action组 196 | /// 197 | private static XmlElement GetAction(XmlDocument xml) { 198 | XmlElement actions = null; 199 | if (xml.GetElementsByTagName("actions").Count != 0) 200 | actions = (XmlElement)xml.GetElementsByTagName("actions")[0]; 201 | else { 202 | actions = xml.CreateElement("actions"); 203 | ((XmlElement)xml.GetElementsByTagName("toast")[0]).AppendChild(actions); 204 | } 205 | return actions; 206 | } 207 | 208 | /// 209 | /// 清除对应App的所有通知 210 | /// 211 | /// app标识 212 | public void ClearHistory(string appid) { 213 | new DesktopNotificationHistoryCompat(appid).Clear(); 214 | } 215 | #endregion 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /ToastCore[netcore]/Notification/NotificationUserInput.cs: -------------------------------------------------------------------------------- 1 | ///------------------------------------------------------------------------------ 2 | /// @ Y_Theta 3 | ///------------------------------------------------------------------------------ 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | namespace ToastCore.Notification { 9 | public class NotificationUserInput : IReadOnlyDictionary { 10 | #region Properties 11 | private NOTIFICATION_USER_INPUT_DATA[] _data; 12 | 13 | internal NotificationUserInput(NOTIFICATION_USER_INPUT_DATA[] data) { 14 | _data = data; 15 | } 16 | 17 | public string this[string key] => _data.First(i => i.Key == key).Value; 18 | 19 | public IEnumerable Keys => _data.Select(i => i.Key); 20 | 21 | public IEnumerable Values => _data.Select(i => i.Value); 22 | 23 | public int Count => _data is null ? 0 : _data.Length; 24 | #endregion 25 | 26 | #region Methods 27 | public bool ContainsKey(string key) { 28 | return _data.Any(i => i.Key == key); 29 | } 30 | 31 | public IEnumerator> GetEnumerator() { 32 | return _data.Select(i => new KeyValuePair(i.Key, i.Value)).GetEnumerator(); 33 | } 34 | 35 | public bool TryGetValue(string key, out string value) { 36 | foreach (var item in _data) { 37 | if (item.Key == key) { 38 | value = item.Value; 39 | return true; 40 | } 41 | } 42 | 43 | value = null; 44 | return false; 45 | } 46 | 47 | IEnumerator IEnumerable.GetEnumerator() { 48 | return GetEnumerator(); 49 | } 50 | #endregion 51 | 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /ToastCore[netcore]/Notification/ToastCommands.cs: -------------------------------------------------------------------------------- 1 | ///------------------------------------------------------------------------------ 2 | /// @ Y_Theta 3 | ///------------------------------------------------------------------------------ 4 | 5 | 6 | namespace ToastCore.Notification { 7 | 8 | /// 9 | /// Toast通知的按钮命令 10 | /// 11 | public struct ToastCommands { 12 | #region Properties 13 | public string Argument; 14 | 15 | public string Content; 16 | #endregion 17 | 18 | #region Constructors 19 | public ToastCommands(string arg, string content) { 20 | Argument = arg; 21 | Content = content; 22 | } 23 | #endregion 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /ToastCore[netcore]/RunningObjectTable.cs: -------------------------------------------------------------------------------- 1 | ///------------------------------------------------------------------------------ 2 | /// @ Y_Theta 3 | ///------------------------------------------------------------------------------ 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Runtime.InteropServices; 7 | using System.Runtime.InteropServices.ComTypes; 8 | using System.Text; 9 | 10 | 11 | namespace ToastCore { 12 | 13 | 14 | public class RunningObjectTable : IDisposable { 15 | #region Properties 16 | #endregion 17 | 18 | #region Methods 19 | private readonly IRunningObjectTable rot; 20 | private bool isDisposed = false; 21 | 22 | public RunningObjectTable() { 23 | Ole32.GetRunningObjectTable(0, out this.rot); 24 | } 25 | 26 | public void Dispose() { 27 | if (this.isDisposed) { 28 | return; 29 | } 30 | 31 | Marshal.ReleaseComObject(this.rot); 32 | this.isDisposed = true; 33 | } 34 | 35 | /// 36 | /// Attempts to register an item in the ROT. 37 | /// 38 | public IDisposable Register(string itemName, object obj) { 39 | IMoniker moniker = CreateMoniker(itemName); 40 | 41 | const int ROTFLAGS_REGISTRATIONKEEPSALIVE = 1; 42 | int regCookie = this.rot.Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, obj, moniker); 43 | 44 | return new RevokeRegistration(rot, regCookie); 45 | } 46 | 47 | /// 48 | /// Attempts to retrieve an item from the ROT. 49 | /// 50 | public object GetObject(string itemName) { 51 | 52 | IMoniker mk = CreateMoniker(itemName); 53 | 54 | int hr = this.rot.GetObject(mk, out object obj); 55 | if (hr != 0) { 56 | Marshal.ThrowExceptionForHR(hr); 57 | } 58 | 59 | return obj; 60 | } 61 | 62 | private void Revoke(int regCookie) { 63 | this.rot.Revoke(regCookie); 64 | } 65 | 66 | private IMoniker CreateMoniker(string itemName) { 67 | Ole32.CreateItemMoniker("!", itemName, out IMoniker mk); 68 | return mk; 69 | } 70 | 71 | 72 | #endregion 73 | 74 | #region Constructors 75 | #endregion 76 | } 77 | 78 | internal class RevokeRegistration : IDisposable { 79 | private readonly IRunningObjectTable rot; 80 | private readonly int regCookie; 81 | 82 | public RevokeRegistration(IRunningObjectTable rot, int regCookie) { 83 | this.rot = rot; 84 | this.regCookie = regCookie; 85 | } 86 | 87 | public void Dispose() { 88 | this.rot.Revoke(this.regCookie); 89 | } 90 | 91 | } 92 | 93 | internal static class Ole32 { 94 | 95 | /// 96 | /// 97 | /// 98 | /// 99 | /// 100 | [DllImport("Ole32.dll")] 101 | static extern int CreateClassMoniker([In] ref Guid rclsid, 102 | out IMoniker ppmk); 103 | 104 | [DllImport(nameof(Ole32))] 105 | public static extern void CreateItemMoniker( 106 | [MarshalAs(UnmanagedType.LPWStr)] string lpszDelim, 107 | [MarshalAs(UnmanagedType.LPWStr)] string lpszItem, 108 | out IMoniker ppmk); 109 | 110 | [DllImport(nameof(Ole32))] 111 | public static extern void GetRunningObjectTable( 112 | int reserved, 113 | out IRunningObjectTable pprot); 114 | } 115 | 116 | } -------------------------------------------------------------------------------- /ToastCore[netcore]/ShellHelper.cs: -------------------------------------------------------------------------------- 1 | ///------------------------------------------------------------------------------ 2 | /// @ Y_Theta 3 | ///------------------------------------------------------------------------------ 4 | using System; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | 8 | using HRESULT = System.Int32; 9 | using CLSID = System.Guid; 10 | using IID = System.Guid; 11 | 12 | namespace ToastCore { 13 | 14 | [StructLayout(LayoutKind.Sequential, Pack = 4)] 15 | public struct PropertyKey { 16 | #region Private Fields 17 | 18 | private Guid formatId; 19 | private Int32 propertyId; 20 | #endregion 21 | 22 | #region Public Construction 23 | 24 | public PropertyKey(Guid formatId, Int32 propertyId) { 25 | this.formatId = formatId; 26 | this.propertyId = propertyId; 27 | } 28 | #endregion 29 | } 30 | 31 | internal enum STGM : long { 32 | STGM_READ = 0x00000000L, 33 | STGM_WRITE = 0x00000001L, 34 | STGM_READWRITE = 0x00000002L, 35 | STGM_SHARE_DENY_NONE = 0x00000040L, 36 | STGM_SHARE_DENY_READ = 0x00000030L, 37 | STGM_SHARE_DENY_WRITE = 0x00000020L, 38 | STGM_SHARE_EXCLUSIVE = 0x00000010L, 39 | STGM_PRIORITY = 0x00040000L, 40 | STGM_CREATE = 0x00001000L, 41 | STGM_CONVERT = 0x00020000L, 42 | STGM_FAILIFTHERE = 0x00000000L, 43 | STGM_DIRECT = 0x00000000L, 44 | STGM_TRANSACTED = 0x00010000L, 45 | STGM_NOSCRATCH = 0x00100000L, 46 | STGM_NOSNAPSHOT = 0x00200000L, 47 | STGM_SIMPLE = 0x08000000L, 48 | STGM_DIRECT_SWMR = 0x00400000L, 49 | STGM_DELETEONRELEASE = 0x04000000L, 50 | } 51 | 52 | [ComImport, 53 | Guid("000214F9-0000-0000-C000-000000000046"), 54 | InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 55 | internal interface IShellLinkW { 56 | UInt32 GetPath( 57 | [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, 58 | int cchMaxPath, 59 | //ref _WIN32_FIND_DATAW pfd, 60 | IntPtr pfd, 61 | uint fFlags); 62 | UInt32 GetIDList(out IntPtr ppidl); 63 | UInt32 SetIDList(IntPtr pidl); 64 | UInt32 GetDescription( 65 | [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, 66 | int cchMaxName); 67 | UInt32 SetDescription( 68 | [MarshalAs(UnmanagedType.LPWStr)] string pszName); 69 | UInt32 GetWorkingDirectory( 70 | [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, 71 | int cchMaxPath 72 | ); 73 | UInt32 SetWorkingDirectory( 74 | [MarshalAs(UnmanagedType.LPWStr)] string pszDir); 75 | UInt32 GetArguments( 76 | [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, 77 | int cchMaxPath); 78 | UInt32 SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); 79 | UInt32 GetHotKey(out short wHotKey); 80 | UInt32 SetHotKey(short wHotKey); 81 | UInt32 GetShowCmd(out uint iShowCmd); 82 | UInt32 SetShowCmd(uint iShowCmd); 83 | UInt32 GetIconLocation( 84 | [Out(), MarshalAs(UnmanagedType.LPWStr)] out StringBuilder pszIconPath, 85 | int cchIconPath, 86 | out int iIcon); 87 | UInt32 SetIconLocation( 88 | [MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, 89 | int iIcon); 90 | UInt32 SetRelativePath( 91 | [MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, 92 | uint dwReserved); 93 | UInt32 Resolve(IntPtr hwnd, uint fFlags); 94 | UInt32 SetPath( 95 | [MarshalAs(UnmanagedType.LPWStr)] string pszFile); 96 | } 97 | 98 | [ComImport] 99 | [Guid("0000010b-0000-0000-C000-000000000046")] 100 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 101 | internal interface IPersistFile { 102 | UInt32 GetCurFile( 103 | [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile 104 | ); 105 | UInt32 IsDirty(); 106 | UInt32 Load( 107 | [MarshalAs(UnmanagedType.LPWStr)] string pszFileName, 108 | [MarshalAs(UnmanagedType.U4)] STGM dwMode); 109 | UInt32 Save( 110 | [MarshalAs(UnmanagedType.LPWStr)] string pszFileName, 111 | bool fRemember); 112 | UInt32 SaveCompleted( 113 | [MarshalAs(UnmanagedType.LPWStr)] string pszFileName); 114 | } 115 | 116 | [ComImport] 117 | [Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")] 118 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 119 | interface IPropertyStore { 120 | UInt32 GetCount([Out] out uint propertyCount); 121 | UInt32 GetAt([In] uint propertyIndex, out PropertyKey key); 122 | UInt32 GetValue([In] ref PropertyKey key, [Out] PropVariant pv); 123 | UInt32 SetValue([In] ref PropertyKey key, [In] PropVariant pv); 124 | UInt32 Commit(); 125 | } 126 | 127 | /// 128 | /// 承载IShellLinkW 129 | /// 130 | [ComImport, 131 | Guid("00021401-0000-0000-C000-000000000046"), 132 | ClassInterface(ClassInterfaceType.None)] 133 | internal class CShellLink { } 134 | 135 | public static class ErrorHelper { 136 | public static void VerifySucceeded(UInt32 hresult) { 137 | if (hresult > 1) { 138 | throw new Exception("Failed with HRESULT: " + hresult.ToString("X")); 139 | } 140 | } 141 | } 142 | 143 | /// 144 | /// 对c++中一种结构的封装 145 | /// 146 | [StructLayout(LayoutKind.Sequential)] 147 | public sealed class PropVariant : IDisposable { 148 | #region Fields 149 | 150 | ushort _valueType; 151 | ushort wReserved1; 152 | ushort wReserved2; 153 | ushort wReserved3; 154 | IntPtr _ptr; 155 | 156 | #endregion 157 | 158 | #region Constructors 159 | public PropVariant(string value) { 160 | if (value == null) { 161 | throw new ArgumentException(); 162 | } 163 | _valueType = (ushort)VarEnum.VT_LPWSTR; 164 | _ptr = Marshal.StringToCoTaskMemUni(value); 165 | } 166 | 167 | public PropVariant(Guid guid) { 168 | if (guid == null) { 169 | throw new ArgumentException(); 170 | } 171 | byte[] bytes = guid.ToByteArray(); 172 | _valueType = (ushort)VarEnum.VT_CLSID; 173 | _ptr = Marshal.AllocCoTaskMem(bytes.Length); 174 | Marshal.Copy(bytes, 0, _ptr, bytes.Length); 175 | } 176 | 177 | #endregion 178 | 179 | #region IDisposable Members 180 | [DllImport("Ole32.dll")] // returns hresult 181 | internal extern static void PropVariantClear([In, Out] PropVariant pvar); 182 | 183 | public void Dispose() { 184 | PropVariantClear(this); 185 | GC.SuppressFinalize(this); 186 | } 187 | 188 | ~PropVariant() { 189 | Dispose(); 190 | } 191 | 192 | #endregion 193 | } 194 | 195 | #if NETCOREAPP3_1 196 | [ComImport] 197 | [Guid("00000001-0000-0000-C000-000000000046")] 198 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 199 | internal interface IClassFactory { 200 | // For HRESULTs use 201 | [PreserveSig] 202 | HRESULT CreateInstance([In] IntPtr pUnkOuter, 203 | [In] ref IID riid, 204 | [Out] out IntPtr ppvObject); 205 | 206 | [PreserveSig] 207 | HRESULT LockServer([In, MarshalAs(UnmanagedType.VariantBool)] bool fLock); 208 | 209 | // HRESULT STDMETHODCALLTYPE CreateInstance( 210 | // /* [unique][in] */ IUnknown *pUnkOuter, 211 | // /* [in] */ REFIID riid, 212 | // /* [iid_is][out] */ void **ppvObject) = 0; 213 | 214 | // virtual /* [local] */ HRESULT STDMETHODCALLTYPE LockServer( 215 | // /* [in] */ BOOL fLock) = 0; 216 | } 217 | 218 | 219 | [ComVisible(true)] 220 | [ClassInterface(ClassInterfaceType.None)] 221 | internal class ClassFactory: IClassFactory { 222 | 223 | private Type _type; 224 | 225 | public ClassFactory(Type type) { 226 | _type = type; 227 | } 228 | 229 | public int CreateInstance([In] IntPtr pUnkOuter, [In] ref CLSID riid, [Out] out IntPtr ppvObject) { 230 | ppvObject = IntPtr.Zero; 231 | object instance = Activator.CreateInstance(_type); 232 | 233 | if (pUnkOuter != IntPtr.Zero) { 234 | // For now no aggregation support - could do Marshal.CreateAggregatedObject? 235 | return 1; 236 | } 237 | if (riid == new Guid("00000001-0000-0000-C000-000000000046")) { 238 | ppvObject = Marshal.GetIUnknownForObject(instance); 239 | } else { 240 | ppvObject = Marshal.GetIUnknownForObject(instance); 241 | HRESULT hrQI = Marshal.QueryInterface(ppvObject, ref riid, out ppvObject); 242 | Marshal.Release(ppvObject); 243 | if (hrQI != 0) { 244 | return 2; 245 | } 246 | } 247 | return 0; 248 | } 249 | 250 | public int LockServer([In, MarshalAs(UnmanagedType.VariantBool)] bool fLock) { 251 | return 0; 252 | } 253 | } 254 | #endif 255 | } 256 | -------------------------------------------------------------------------------- /ToastCore[netcore]/ToastCore[netcore].csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1;net45 5 | ToastCore 6 | 7 | 8 | 9 | TRACE;NETCOREAPP3_1 10 | 11 | 12 | 13 | TRACE;NET45 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 4.7.0 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | D:\Windows Kits\10\References\10.0.18362.0\Windows.Foundation.FoundationContract\3.0.0.0\Windows.Foundation.FoundationContract.winmd 41 | false 42 | 43 | 44 | D:\Windows Kits\10\References\10.0.18362.0\Windows.Foundation.UniversalApiContract\8.0.0.0\Windows.Foundation.UniversalApiContract.winmd 45 | false 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /ToastHelper.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29025.244 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ToastHelper", "ToastHelper\ToastHelper.csproj", "{7A012F81-7D1C-4677-952D-708874E4346F}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ToastCore", "ToastCore\ToastCore.csproj", "{18F57AA0-E5ED-4569-A074-A0C622D3D52B}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ToastCore[netcore]", "ToastCore[netcore]\ToastCore[netcore].csproj", "{508A4E59-C433-40F1-B708-A945077B1625}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{79B2EA72-F1A6-4115-97FD-7658D41E1FA3}" 13 | ProjectSection(SolutionItems) = preProject 14 | ReadMe.md = ReadMe.md 15 | EndProjectSection 16 | EndProject 17 | Global 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|Any CPU = Debug|Any CPU 20 | Debug|ARM = Debug|ARM 21 | Debug|ARM64 = Debug|ARM64 22 | Debug|x64 = Debug|x64 23 | Debug|x86 = Debug|x86 24 | Release|Any CPU = Release|Any CPU 25 | Release|ARM = Release|ARM 26 | Release|ARM64 = Release|ARM64 27 | Release|x64 = Release|x64 28 | Release|x86 = Release|x86 29 | EndGlobalSection 30 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 31 | {7A012F81-7D1C-4677-952D-708874E4346F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {7A012F81-7D1C-4677-952D-708874E4346F}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {7A012F81-7D1C-4677-952D-708874E4346F}.Debug|ARM.ActiveCfg = Debug|Any CPU 34 | {7A012F81-7D1C-4677-952D-708874E4346F}.Debug|ARM.Build.0 = Debug|Any CPU 35 | {7A012F81-7D1C-4677-952D-708874E4346F}.Debug|ARM64.ActiveCfg = Debug|Any CPU 36 | {7A012F81-7D1C-4677-952D-708874E4346F}.Debug|ARM64.Build.0 = Debug|Any CPU 37 | {7A012F81-7D1C-4677-952D-708874E4346F}.Debug|x64.ActiveCfg = Debug|Any CPU 38 | {7A012F81-7D1C-4677-952D-708874E4346F}.Debug|x64.Build.0 = Debug|Any CPU 39 | {7A012F81-7D1C-4677-952D-708874E4346F}.Debug|x86.ActiveCfg = Debug|Any CPU 40 | {7A012F81-7D1C-4677-952D-708874E4346F}.Debug|x86.Build.0 = Debug|Any CPU 41 | {7A012F81-7D1C-4677-952D-708874E4346F}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {7A012F81-7D1C-4677-952D-708874E4346F}.Release|Any CPU.Build.0 = Release|Any CPU 43 | {7A012F81-7D1C-4677-952D-708874E4346F}.Release|ARM.ActiveCfg = Release|Any CPU 44 | {7A012F81-7D1C-4677-952D-708874E4346F}.Release|ARM.Build.0 = Release|Any CPU 45 | {7A012F81-7D1C-4677-952D-708874E4346F}.Release|ARM64.ActiveCfg = Release|Any CPU 46 | {7A012F81-7D1C-4677-952D-708874E4346F}.Release|ARM64.Build.0 = Release|Any CPU 47 | {7A012F81-7D1C-4677-952D-708874E4346F}.Release|x64.ActiveCfg = Release|Any CPU 48 | {7A012F81-7D1C-4677-952D-708874E4346F}.Release|x64.Build.0 = Release|Any CPU 49 | {7A012F81-7D1C-4677-952D-708874E4346F}.Release|x86.ActiveCfg = Release|Any CPU 50 | {7A012F81-7D1C-4677-952D-708874E4346F}.Release|x86.Build.0 = Release|Any CPU 51 | {18F57AA0-E5ED-4569-A074-A0C622D3D52B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 52 | {18F57AA0-E5ED-4569-A074-A0C622D3D52B}.Debug|Any CPU.Build.0 = Debug|Any CPU 53 | {18F57AA0-E5ED-4569-A074-A0C622D3D52B}.Debug|ARM.ActiveCfg = Debug|Any CPU 54 | {18F57AA0-E5ED-4569-A074-A0C622D3D52B}.Debug|ARM64.ActiveCfg = Debug|Any CPU 55 | {18F57AA0-E5ED-4569-A074-A0C622D3D52B}.Debug|x64.ActiveCfg = Debug|Any CPU 56 | {18F57AA0-E5ED-4569-A074-A0C622D3D52B}.Debug|x86.ActiveCfg = Debug|Any CPU 57 | {18F57AA0-E5ED-4569-A074-A0C622D3D52B}.Release|Any CPU.ActiveCfg = Release|Any CPU 58 | {18F57AA0-E5ED-4569-A074-A0C622D3D52B}.Release|Any CPU.Build.0 = Release|Any CPU 59 | {18F57AA0-E5ED-4569-A074-A0C622D3D52B}.Release|ARM.ActiveCfg = Release|Any CPU 60 | {18F57AA0-E5ED-4569-A074-A0C622D3D52B}.Release|ARM64.ActiveCfg = Release|Any CPU 61 | {18F57AA0-E5ED-4569-A074-A0C622D3D52B}.Release|x64.ActiveCfg = Release|Any CPU 62 | {18F57AA0-E5ED-4569-A074-A0C622D3D52B}.Release|x86.ActiveCfg = Release|Any CPU 63 | {508A4E59-C433-40F1-B708-A945077B1625}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 64 | {508A4E59-C433-40F1-B708-A945077B1625}.Debug|Any CPU.Build.0 = Debug|Any CPU 65 | {508A4E59-C433-40F1-B708-A945077B1625}.Debug|ARM.ActiveCfg = Debug|Any CPU 66 | {508A4E59-C433-40F1-B708-A945077B1625}.Debug|ARM.Build.0 = Debug|Any CPU 67 | {508A4E59-C433-40F1-B708-A945077B1625}.Debug|ARM64.ActiveCfg = Debug|Any CPU 68 | {508A4E59-C433-40F1-B708-A945077B1625}.Debug|ARM64.Build.0 = Debug|Any CPU 69 | {508A4E59-C433-40F1-B708-A945077B1625}.Debug|x64.ActiveCfg = Debug|Any CPU 70 | {508A4E59-C433-40F1-B708-A945077B1625}.Debug|x64.Build.0 = Debug|Any CPU 71 | {508A4E59-C433-40F1-B708-A945077B1625}.Debug|x86.ActiveCfg = Debug|Any CPU 72 | {508A4E59-C433-40F1-B708-A945077B1625}.Debug|x86.Build.0 = Debug|Any CPU 73 | {508A4E59-C433-40F1-B708-A945077B1625}.Release|Any CPU.ActiveCfg = Release|Any CPU 74 | {508A4E59-C433-40F1-B708-A945077B1625}.Release|Any CPU.Build.0 = Release|Any CPU 75 | {508A4E59-C433-40F1-B708-A945077B1625}.Release|ARM.ActiveCfg = Release|Any CPU 76 | {508A4E59-C433-40F1-B708-A945077B1625}.Release|ARM.Build.0 = Release|Any CPU 77 | {508A4E59-C433-40F1-B708-A945077B1625}.Release|ARM64.ActiveCfg = Release|Any CPU 78 | {508A4E59-C433-40F1-B708-A945077B1625}.Release|ARM64.Build.0 = Release|Any CPU 79 | {508A4E59-C433-40F1-B708-A945077B1625}.Release|x64.ActiveCfg = Release|Any CPU 80 | {508A4E59-C433-40F1-B708-A945077B1625}.Release|x64.Build.0 = Release|Any CPU 81 | {508A4E59-C433-40F1-B708-A945077B1625}.Release|x86.ActiveCfg = Release|Any CPU 82 | {508A4E59-C433-40F1-B708-A945077B1625}.Release|x86.Build.0 = Release|Any CPU 83 | EndGlobalSection 84 | GlobalSection(SolutionProperties) = preSolution 85 | HideSolutionNode = FALSE 86 | EndGlobalSection 87 | GlobalSection(ExtensibilityGlobals) = postSolution 88 | SolutionGuid = {EB7161A1-3538-4E93-BAA4-ACE5690F773D} 89 | EndGlobalSection 90 | EndGlobal 91 | -------------------------------------------------------------------------------- /ToastHelper/App.xaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 9 | 10 | 11 | 16 | 21 | 22 | 23 | 24 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /ToastHelper/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using Hardcodet.Wpf.TaskbarNotification; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Diagnostics; 6 | using System.Windows; 7 | using System.Windows.Input; 8 | 9 | namespace ToastHelper { 10 | /// 11 | /// App.xaml 的交互逻辑 12 | /// 13 | public partial class App : Application { 14 | protected override void OnStartup(StartupEventArgs e) 15 | { 16 | _taskbar = (TaskbarIcon)FindResource("Taskbar"); 17 | base.OnStartup(e); 18 | for (int i = 1; i == e.Args.Length; i++) 19 | { 20 | if (e.Args[i - 1] == "--silence") Common.IsSilence = true; 21 | } 22 | } 23 | 24 | private TaskbarIcon _taskbar; 25 | 26 | private void PauseChecking(object sender, RoutedEventArgs e) 27 | { 28 | Common.IsPausingScan = true; 29 | } 30 | 31 | private void UnPauseChecking(object sender, RoutedEventArgs e) 32 | { 33 | Common.IsPausingScan = false; 34 | } 35 | private void GetAutoStart(object sender, RoutedEventArgs e) 36 | { 37 | AutoStart.SetAutoStart(); 38 | } 39 | 40 | private void CancelAutoStart(object sender, RoutedEventArgs e) 41 | { 42 | AutoStart.UnsetAutoStart(); 43 | } 44 | } 45 | public class DelegateCommand : ICommand 46 | { 47 | public Action CommandAction { get; set; } 48 | public Func CanExecuteFunc { get; set; } 49 | 50 | public void Execute(object parameter) 51 | { 52 | CommandAction(); 53 | } 54 | 55 | public bool CanExecute(object parameter) 56 | { 57 | return CanExecuteFunc == null || CanExecuteFunc(); 58 | } 59 | 60 | public event EventHandler CanExecuteChanged 61 | { 62 | add { CommandManager.RequerySuggested += value; } 63 | remove { CommandManager.RequerySuggested -= value; } 64 | } 65 | } 66 | 67 | public class NotifyIconViewModel 68 | { 69 | public ICommand ExitApplicationCommand 70 | { 71 | get 72 | { 73 | return new DelegateCommand { CommandAction = () => Application.Current.Shutdown() }; 74 | } 75 | } 76 | 77 | private bool _isPauseChecking = false; 78 | public bool IsPauseChecking 79 | { 80 | get { return _isPauseChecking; } 81 | set 82 | { 83 | _isPauseChecking = value; 84 | OnPropertyChanged("IsPauseChecking"); 85 | } 86 | } 87 | 88 | private bool _isAutoStart = AutoStart.IsAutoStart(); 89 | public bool IsAutoStart 90 | { 91 | get { return _isAutoStart; } 92 | set 93 | { 94 | _isAutoStart = value; 95 | OnPropertyChanged("IsAutoStart"); 96 | } 97 | } 98 | 99 | public event PropertyChangedEventHandler PropertyChanged; 100 | 101 | // Create the OnPropertyChanged method to raise the event 102 | protected void OnPropertyChanged(string name) 103 | { 104 | PropertyChangedEventHandler handler = PropertyChanged; 105 | if (handler != null) 106 | { 107 | handler(this, new PropertyChangedEventArgs(name)); 108 | } 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /ToastHelper/AutoStart.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | using System.Diagnostics; 7 | using System.Reflection; 8 | 9 | namespace ToastHelper 10 | { 11 | class AutoStart 12 | { 13 | private static void CreateShortcut(string lnkFilePath, string args) 14 | { 15 | var shellType = Type.GetTypeFromProgID("WScript.Shell"); 16 | dynamic shell = Activator.CreateInstance(shellType); 17 | var shortcut = shell.CreateShortcut(lnkFilePath); 18 | shortcut.TargetPath = Assembly.GetEntryAssembly().Location; 19 | shortcut.Arguments = args; 20 | shortcut.WorkingDirectory = AppDomain.CurrentDomain.SetupInformation.ApplicationBase; 21 | shortcut.Save(); 22 | } 23 | 24 | public static void SetAutoStart() 25 | { 26 | var startupPath = Path.Combine( 27 | Environment.GetFolderPath(Environment.SpecialFolder.Startup), 28 | "ClipBoardQrHelper.lnk"); 29 | CreateShortcut(startupPath, "--silence"); 30 | } 31 | 32 | public static void UnsetAutoStart() 33 | { 34 | var startupPath = Path.Combine( 35 | Environment.GetFolderPath(Environment.SpecialFolder.Startup), 36 | "ClipBoardQrHelper.lnk"); 37 | File.Delete(startupPath); 38 | } 39 | 40 | public static bool IsAutoStart() 41 | { 42 | var startupPath = Path.Combine( 43 | Environment.GetFolderPath(Environment.SpecialFolder.Startup), 44 | "ClipBoardQrHelper.lnk"); 45 | return File.Exists(startupPath); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ToastHelper/ClipboardManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Windows; 4 | using System.Windows.Interop; 5 | 6 | public class ClipboardManager 7 | { 8 | internal static class NativeMethods 9 | { 10 | // See http://msdn.microsoft.com/en-us/library/ms649021%28v=vs.85%29.aspx 11 | public const int WM_CLIPBOARDUPDATE = 0x031D; 12 | public static IntPtr HWND_MESSAGE = new IntPtr(-3); 13 | 14 | // See http://msdn.microsoft.com/en-us/library/ms632599%28VS.85%29.aspx#message_only 15 | [DllImport("user32.dll", SetLastError = true)] 16 | [return: MarshalAs(UnmanagedType.Bool)] 17 | public static extern bool AddClipboardFormatListener(IntPtr hwnd); 18 | } 19 | 20 | public event EventHandler ClipboardChanged; 21 | 22 | public ClipboardManager(Window windowSource) 23 | { 24 | HwndSource source = PresentationSource.FromVisual(windowSource) as HwndSource; 25 | if (source == null) 26 | { 27 | throw new ArgumentException( 28 | "Window source MUST be initialized first, such as in the Window's OnSourceInitialized handler." 29 | , nameof(windowSource)); 30 | } 31 | 32 | source.AddHook(WndProc); 33 | 34 | // get window handle for interop 35 | IntPtr windowHandle = new WindowInteropHelper(windowSource).Handle; 36 | 37 | // register for clipboard events 38 | NativeMethods.AddClipboardFormatListener(windowHandle); 39 | } 40 | 41 | private void OnClipboardChanged() 42 | { 43 | ClipboardChanged?.Invoke(this, EventArgs.Empty); 44 | } 45 | 46 | private static readonly IntPtr WndProcSuccess = IntPtr.Zero; 47 | 48 | private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) 49 | { 50 | if (msg == NativeMethods.WM_CLIPBOARDUPDATE) 51 | { 52 | OnClipboardChanged(); 53 | handled = true; 54 | } 55 | 56 | return WndProcSuccess; 57 | } 58 | } -------------------------------------------------------------------------------- /ToastHelper/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  11 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /ToastHelper/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Windows; 4 | using System.Windows.Media.Imaging; 5 | using ToastCore.Notification; 6 | using System.Runtime.InteropServices; 7 | using System.Runtime.InteropServices.ComTypes; 8 | using System.Diagnostics; 9 | using ZXing; 10 | using System.Drawing; 11 | using System.IO; 12 | 13 | namespace ToastHelper { 14 | public static class Common // static 不是必须 15 | { 16 | private static bool _IsPausingScan = false; 17 | public static bool IsPausingScan 18 | { 19 | get { return _IsPausingScan; } 20 | set { _IsPausingScan = value; } 21 | } 22 | 23 | private static bool _IsSilence = false; 24 | public static bool IsSilence 25 | { 26 | get { return _IsSilence; } 27 | set { _IsSilence = value; } 28 | } 29 | } 30 | 31 | public partial class MainWindow : Window { 32 | private ToastManager _manager; 33 | private Action _notify; 34 | private readonly IRunningObjectTable rot; 35 | 36 | [DllImport("Ole32.dll")] 37 | static extern int CreateClassMoniker([In] ref Guid rclsid, 38 | out IMoniker ppmk); 39 | 40 | [DllImport("Ole32.dll")] 41 | public static extern void GetRunningObjectTable( 42 | int reserved, 43 | out IRunningObjectTable pprot); 44 | 45 | [DllImport("Ole32.dll")] 46 | static extern int CoRegisterClassObject( 47 | [MarshalAs(UnmanagedType.LPStruct)] Guid rclsid, 48 | [MarshalAs(UnmanagedType.IUnknown)] object pUnk, 49 | uint dwClsContext, 50 | uint flags, 51 | out uint lpdwRegister); 52 | 53 | public MainWindow() { 54 | InitializeComponent(); 55 | _manager = new ToastManager(); 56 | _manager.Init("ClipBoard Qr Helper"); 57 | ToastManager.ToastCallback += ToastManager_ToastCallback; 58 | GetRunningObjectTable(0, out this.rot); 59 | if (Common.IsSilence == false) 60 | { 61 | _notify = new Action(() => _manager.Notify("欢迎使用,我在托盘为你服务哦!", "ClipBoard Qr Helper")); ; ; 62 | _notify?.BeginInvoke(null, null); 63 | } 64 | } 65 | 66 | private void ToastManager_ToastCallback(string app, string arg, List> kvs) { 67 | App.Current.Dispatcher.Invoke(() => { 68 | if (arg.StartsWith("copy:")) Clipboard.SetText (arg.Substring(5)); 69 | if (arg.StartsWith("goUrl:")) Process.Start(arg.Substring(6)); 70 | }); 71 | } 72 | 73 | protected override void OnSourceInitialized(EventArgs e) 74 | { 75 | base.OnSourceInitialized(e); 76 | var windowClipboardManager = new ClipboardManager(this); 77 | windowClipboardManager.ClipboardChanged += ClipboardChanged; 78 | this.Visibility = Visibility.Hidden; 79 | } 80 | 81 | private void ClipboardChanged(object sender, EventArgs e) 82 | { 83 | if (Clipboard.ContainsImage() && Common.IsPausingScan == false) 84 | { 85 | Bitmap image = null; 86 | try { image = BitmapFromSource(Clipboard.GetImage()); } catch { 87 | try { image = (Bitmap)Image.FromFile(Clipboard.GetFileDropList()[0]); } catch 88 | { 89 | image = null; 90 | } 91 | } 92 | if (image != null) 93 | { 94 | IBarcodeReader reader = new BarcodeReader(); 95 | var barcodeBitmap = image; 96 | var result = reader.Decode(barcodeBitmap); 97 | if (result != null) 98 | { 99 | if (result.Text.ToLower().StartsWith("http://") || result.Text.ToLower().StartsWith("https://")) 100 | { 101 | _notify = new Action(() => _manager.Notify("从剪贴板图片中读取到了二维码信息", result.Text, new ToastCommands[] { new ToastCommands { Content = "复制", Argument = "copy:" + result.Text } }, new ToastCommands[] { new ToastCommands { Content = "前往", Argument = "goUrl:" + result.Text } })); ; ; 102 | } 103 | else 104 | { 105 | _notify = new Action(() => _manager.Notify("从剪贴板图片中读取到了二维码信息", result.Text, new ToastCommands[] { new ToastCommands { Content = "复制", Argument = "copy:" + result.Text } }, new ToastCommands[] { new ToastCommands { Content = "忽略", Argument = "nothing" } })); ; ; 106 | } 107 | _notify?.BeginInvoke(null, null); 108 | } 109 | } 110 | } 111 | } 112 | 113 | private System.Drawing.Bitmap BitmapFromSource(BitmapSource bitmapsource) 114 | { 115 | System.Drawing.Bitmap bitmap; 116 | using (MemoryStream outStream = new MemoryStream()) 117 | { 118 | BitmapEncoder enc = new BmpBitmapEncoder(); 119 | enc.Frames.Add(BitmapFrame.Create(bitmapsource)); 120 | enc.Save(outStream); 121 | bitmap = new System.Drawing.Bitmap(outStream); 122 | } 123 | return bitmap; 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /ToastHelper/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // 有关程序集的一般信息由以下 8 | // 控制。更改这些特性值可修改 9 | // 与程序集关联的信息。 10 | [assembly: AssemblyTitle("ClipBoardQrHelper")] 11 | [assembly: AssemblyDescription("ClipBoardQrHelper")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("Ray")] 14 | [assembly: AssemblyProduct("ClipBoardQrHelper")] 15 | [assembly: AssemblyCopyright("© Ray 2014 - 2021.")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // 将 ComVisible 设置为 false 会使此程序集中的类型 20 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 21 | //请将此类型的 ComVisible 特性设置为 true。 22 | [assembly: ComVisible(true)] 23 | 24 | //若要开始生成可本地化的应用程序,请设置 25 | //.csproj 文件中的 CultureYouAreCodingWith 26 | //例如,如果您在源文件中使用的是美国英语, 27 | //使用的是美国英语,请将 设置为 en-US。 然后取消 28 | //对以下 NeutralResourceLanguage 特性的注释。 更新 29 | //以下行中的“en-US”以匹配项目文件中的 UICulture 设置。 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //主题特定资源词典所处位置 36 | //(未在页面中找到资源时使用, 37 | //或应用程序资源字典中找到时使用) 38 | ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置 39 | //(未在页面中找到资源时使用, 40 | //、应用程序或任何主题专用资源字典中找到时使用) 41 | )] 42 | 43 | 44 | // 程序集的版本信息由下列四个值组成: 45 | // 46 | // 主版本 47 | // 次版本 48 | // 生成号 49 | // 修订号 50 | // 51 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 52 | //通过使用 "*",如下所示: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] 56 | -------------------------------------------------------------------------------- /ToastHelper/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace ToastHelper.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// 一个强类型的资源类,用于查找本地化的字符串等。 17 | /// 18 | // 此类是由 StronglyTypedResourceBuilder 19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 20 | // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen 21 | // (以 /str 作为命令选项),或重新生成 VS 项目。 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// 返回此类使用的缓存的 ResourceManager 实例。 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ToastHelper.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// 重写当前线程的 CurrentUICulture 属性,对 51 | /// 使用此强类型资源类的所有资源查找执行重写。 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ToastHelper/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /ToastHelper/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace ToastHelper.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ToastHelper/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /ToastHelper/QR code.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BH4HPA/ClipBoard-Qr-Helper/1a547e96101c2664ff4ef0277ee63a1ae053e4d3/ToastHelper/QR code.ico -------------------------------------------------------------------------------- /ToastHelper/ToastHelper.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {7A012F81-7D1C-4677-952D-708874E4346F} 8 | WinExe 9 | ToastHelper 10 | ClipBoardQrHelper 11 | v4.7 12 | 512 13 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 4 15 | true 16 | true 17 | 18 | 19 | true 20 | publish\ 21 | true 22 | Web 23 | true 24 | Foreground 25 | 7 26 | Days 27 | false 28 | false 29 | true 30 | https://static.r-ay.cn/program/clipboardqrhelper/ 31 | https://github.com/ENDsoft233/ClipBoard-Qr-Helper 32 | https://github.com/ENDsoft233/ClipBoard-Qr-Helper 33 | ClipBoard Qr Helper 34 | Ray 35 | ClipBoard Qr Helper 36 | false 37 | 1 38 | 0.1.3.%2a 39 | false 40 | true 41 | true 42 | 43 | 44 | AnyCPU 45 | true 46 | full 47 | false 48 | ..\Test\ 49 | DEBUG;TRACE 50 | prompt 51 | 4 52 | 53 | 54 | AnyCPU 55 | pdbonly 56 | true 57 | bin\Release\ 58 | TRACE 59 | prompt 60 | 4 61 | 62 | 63 | QR code.ico 64 | 65 | 66 | 88B9E98231B32190CE0FF0BD70BE1D73E57ACF52 67 | 68 | 69 | ToastHelper_TemporaryKey.pfx 70 | 71 | 72 | true 73 | 74 | 75 | true 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 4.0 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | MSBuild:Compile 97 | Designer 98 | 99 | 100 | 101 | 102 | 103 | MSBuild:Compile 104 | Designer 105 | 106 | 107 | App.xaml 108 | Code 109 | 110 | 111 | MainWindow.xaml 112 | Code 113 | 114 | 115 | 116 | 117 | Code 118 | 119 | 120 | True 121 | True 122 | Resources.resx 123 | 124 | 125 | True 126 | Settings.settings 127 | True 128 | 129 | 130 | ResXFileCodeGenerator 131 | Resources.Designer.cs 132 | 133 | 134 | SettingsSingleFileGenerator 135 | Settings.Designer.cs 136 | 137 | 138 | 139 | 140 | 141 | {508a4e59-c433-40f1-b708-a945077b1625} 142 | ToastCore[netcore] 143 | 144 | 145 | 146 | 147 | False 148 | Microsoft .NET Framework 4.7 %28x86 和 x64%29 149 | true 150 | 151 | 152 | False 153 | .NET Framework 3.5 SP1 154 | false 155 | 156 | 157 | 158 | 159 | 1.0.8 160 | 161 | 162 | 4.6.0 163 | 164 | 165 | 4.5.0 166 | 167 | 168 | 0.16.6 169 | 170 | 171 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /ToastHelper/ToastManager.cs: -------------------------------------------------------------------------------- 1 | ///------------------------------------------------------------------------------ 2 | /// @ Y_Theta 3 | ///------------------------------------------------------------------------------ 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Runtime.InteropServices; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using System.Xml; 11 | using ToastCore.Notification; 12 | 13 | namespace ToastHelper { 14 | 15 | [ClassInterface(ClassInterfaceType.None)] 16 | [ComSourceInterfaces(typeof(INotificationActivationCallback))] 17 | [Guid("7ddba60f-e2f0-4373-8098-0eafb79ba54a"), ComVisible(true)] 18 | public class ToastManager : NotificationService { 19 | #region Properties 20 | #endregion 21 | 22 | #region Methods 23 | public override void OnActivated(string arguments, NotificationUserInput userInput, string appUserModelId) { 24 | base.OnActivated(arguments, userInput, appUserModelId); 25 | } 26 | #endregion 27 | 28 | #region Constructors 29 | #endregion 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # ClipBoard Qr Helper 剪贴板二维码助手 2 | 3 | It will scan the image which on the clipboard automatically. 4 | 5 | 它将自动识别剪贴板上刚复制的二维码图片。 6 | 7 | ## Environment 环境 8 | 9 | Windows 10, .NET Framework 4.7. - test passed. 测试通过。 10 | 11 | ## Usage 使用方法 12 | 13 | [简体中文(中国)](https://github.com/ENDsoft233/ClipBoard-Qr-Helper/blob/main/readme.md#zh_cn) | [English(US)](https://github.com/ENDsoft233/ClipBoard-Qr-Helper/blob/main/readme.md#en_us) 14 | 15 | ### zh_cn 16 | 17 | 下载 Release 中的 Setup.exe 或 Portable.zip。分别双击打开或解压缩并打开 `ClipBoard Qr Helper.exe` 。 18 | 19 | 如果环境适合,软件将推送第一条 Toast Notification ,你可以通过添加启动项 `--silence` 禁用该通知。 20 | 21 | 将图片或者图片文件复制到剪贴板,软件将自动识别二维码,如果识别出结果,将自动推送通知,反之则无操作。 22 | 23 | 建议搭配截图软件使用,先截出需要识别的二维码放置在剪贴板,再使用本软件识别。 24 | 25 | 识别可以暂停,右击托盘中的软件图标即可暂停识别。 26 | 27 | 托盘菜单中的开机自启动勾选后将在开始菜单的启动目录新建一个本软件的快捷方式用于自启动。该快捷方式指向 `ClipBoard Qr Helper.exe` ,所以不会检查更新。 28 | 29 | 双击托盘图标可以快速退出。 30 | 31 | ### en_us 32 | 33 | Download the setup.exe or Portable.zip in release, and double click to open or unzip the zip then open `ClipBoard Qr Helper.exe` . 34 | 35 | If your Environment is ok, the program will push a toast notification. You can use the `--silence` arg to disable the notification. 36 | 37 | Copy the image or image file, the program will automatically scan the image, and push the notification if result is not empty. 38 | 39 | Double click the icon in taskbar will exit the program. 40 | 41 | ## Thanks Lists 鸣谢 42 | 43 | - https://github.com/20154530/ToastHelper providing the implementation of the Toast Notification. 提供原生通知支持。 44 | 45 | - https://github.com/zxing/zxing providing the implementation of scaning the qr code. 提供本地二维码识别支持。 46 | --------------------------------------------------------------------------------