300 likes | 544 Views
DEV304. Advanced Programming Patterns for Windows 7. Kate Gregory Gregory Consulting www.gregcons.com/kateblog , @ gregcons. Agenda. Libraries Tasks that set Status or send commands Restart and Recovery Trigger Started Services Managed code only. Call to Action.
E N D
DEV304 Advanced Programming Patterns for Windows 7 Kate Gregory Gregory Consulting www.gregcons.com/kateblog, @gregcons
Agenda • Libraries • Tasks that set Status or send commands • Restart and Recovery • Trigger Started Services • Managed code only
Call to Action • Windows 7 has been here for almost two years • You’ve made your app work on Windows 7 • UAC • DPI scaling • Hardcoded file paths • You’ve lit up your app with visuals • Jumplists • Taskbar overlays • Ribbon • Now take the next step
Windows 7 is more than eye candy • Stop polling! You can be notified • Power management • Network awareness • Service triggers • Be considerate • Restart and recovery • Work the way the user thinks • Libraries • Jumplists
Windows 7 Recipes • Samples on Code Gallery • Yours to use, change, learn from • Dependency on Code Pack • http://code.msdn.microsoft.com/Windows-7-Taskbar-Single-4120eafd • http://code.msdn.microsoft.com/Windows-Restart-and-cc461aa9 • http://code.msdn.microsoft.com/Windows-Trigger-Start-f75f0fbb
Libraries • Aggregation of (pointers to) folders • Don’t have to be on the same machine • Users can control what folders are in a library • More control for them, less coding for you
Libraries demo
Create and Manage Libraries • var lib = new ShellLibrary("KateDemo", false); • lib.Dispose(); • ShellLibrary.ShowManageLibraryUI("KateDemo", • new WindowInteropHelper(this).Handle, • "Demo Library Manager", "Manage the KateDemo Library", • true);
Delete a Library • string librariesPath = Path.Combine( • Environment.GetFolderPath( • Environment.SpecialFolder.ApplicationData), • ShellLibrary.LibrariesKnownFolder.RelativePath); • string libraryPath = Path.Combine(librariesPath, "KateDemo"); • string libraryFullPath = Path.ChangeExtension(libraryPath, • "library-ms"); • File.Delete(libraryFullPath);
Use a Library to open files • using (varlib = ShellLibrary.Load("KateDemo", true)) • { • varpickLocationDialog = new CommonOpenFileDialog(); • pickLocationDialog.Title = "Open a file"; • pickLocationDialog.DefaultDirectoryShellContainer = • ShellFolder.FromParsingName(lib.ParsingName) as ShellContainer; • pickLocationDialog.InitialDirectoryShellContainer = • ShellFolder.FromParsingName(lib.ParsingName) as ShellContainer; • pickLocationDialog.AllowNonFileSystemItems = false; • pickLocationDialog.IsFolderPicker = false; • pickLocationDialog.Multiselect = false; • var result = pickLocationDialog.ShowDialog(); • if (result == CommonFileDialogResult.Cancel) { MessageBox.Show("You cancelled"); } • else { MessageBox.Show("You selected " + pickLocationDialog.FileName); }
Watching a library • watcher = new ShellObjectWatcher(lib, true); • watcher.AllEvents += Watcher_LibraryChanged; • watcher.Start(); • void Watcher_LibraryChanged(object sender, • ShellObjectNotificationEventArgsargs) • { • // args will tell you what’s going on • . . .
Tasks • Jumplist tasks launch another exe • Open the help in help viewer, the readme in notepad • Launch a companion app • But what if you want tasks to communicate to the running app? • The exe you launch is your own exe again • Has to be modified to be “single instance”
Single Instance for Tasks • SingleInstanceManager manager = • SingleInstanceManager.Initialize(GetSingleInstanceManagerSetup()); • // . . . • private static SingleInstanceManagerSetupGetSingleInstanceManagerSetup() • { • return new SingleInstanceManagerSetup("WpfSample") • { • ArgumentsHandler = • args => ((App)Application.Current).ProcessCommandLineArgs(args), • ArgumentsHandlerInvoker = new ApplicationDispatcherInvoker(), • DelivaryFailureNotification = • ex => MessageBox.Show(ex.Message, "An error occurred") • }; • }
Adding Tasks to the Jumplist • string cmdPath = Assembly.GetEntryAssembly().Location; • JumpListjumpList = JumpList.CreateJumpList(); • jumpList.AddUserTasks( • new JumpListLink(cmdPath, Status.Online.ToString()) • { Arguments = Status.Online.ToString() }, • new JumpListLink(cmdPath, Status.Away.ToString()) • { Arguments = Status.Away.ToString() }, • new JumpListLink(cmdPath, Status.Busy.ToString()) • { Arguments = Status.Busy.ToString() }); • jumpList.Refresh();
Restart and Recovery • Prevent lost data • Your own code is called when app is terminating • Store ephemeral data • On startup, check for a restart hint • Restore ephemeral data • Problem: too much freedom • What to store, and where? • File format • How to structure restart hint?
Restart and Recovery Recipe • Leverages Serialization • You write a document class, call it anything, add attribute • Use generics to instantiate a helper based on that document • Methods of the helper • Check for restart • Register for restart and recovery • Helper takes care of the rest • naming the file • serializing into or out of it (XML or Binary) • Constructing the restart hint
Restart and Recovery demo
Restart and Recovery • private RestartRecoveryHelper<SampleDocument> rrh; • // ... • rrh = new RestartRecoveryHelper<SampleDocument>(); • SampleDocumentrecovereddoc = rrh.CheckForRestart(); • if (recovereddoc != null) • { • doc = recovereddoc; • // ... • } • rrh.RegisterForRestartAndRecovery("TestHarness", • "Recover", doc, 50000, • FileType.Xml, • RestartRestrictions.None);
Trigger Started Services • Windows Services consume a significant amount of memory and CPU • Autostarted services make startup slow • Delayed start really didn’t work • Trigger started services can be ideal • More responsive (happy users) • No wasted CPU waking and polling (happy users) • Less code to write (happy developers)
Trigger Started Services Recipe • A TriggerStart class on which you set triggers • It configures the service for you after it’s installed • Helper classes for triggers • Join or leave domain • Open or close a specific port on the firewall • Group policy changes • First IP address is gained or last IP address is lost • A specific ETW custom event is raised • A specific device (eg USB drive) is connected or disconnected
Handle the After Install Event • TriggerStartts = new • TriggerStart(USBServiceInstaller.ServiceName); • GuidGuid_USBDevice = new Guid( • "53f56307-b6bf-11d0-94f2-00a0c91efb8b"); • const string DeviceName = @"USBSTOR\GenDisk"; • ts.Triggers.Add( • new DeviceInterfaceArrivalTrigger( • ServiceTriggerAction.ServiceStart, • Guid_USBDevice,DeviceName)); • ts.CommitChangesToService();
Next Steps for You • Make your Windows app a Windows 7 app • Code Pack can help, or WPF4 • Think about going beyond the visual • Power awareness, network awareness • Libraries • Restart and Recovery • Trigger Started Services • Take advantage of what the platform has to offer!
DEV Track Resources • http://www.microsoft.com/visualstudio • http://www.microsoft.com/visualstudio/en-us/lightswitch • http://www.microsoft.com/expression/ • http://blogs.msdn.com/b/somasegar/ • http://blogs.msdn.com/b/bharry/ • http://www.microsoft.com/sqlserver/en/us/default.aspx • http://www.facebook.com/visualstudio
Resources • Connect. Share. Discuss. http://northamerica.msteched.com Learning • Sessions On-Demand & Community • Microsoft Certification & Training Resources www.microsoft.com/teched www.microsoft.com/learning • Resources for IT Professionals • Resources for Developers http://microsoft.com/technet http://microsoft.com/msdn