Example #1
0
        private static List <_ShellObjectPair> GenerateJumpItems(Standard.IObjectArray shellObjects)
        {
            List <_ShellObjectPair> list = new List <_ShellObjectPair>();
            Guid riid  = new Guid("00000000-0000-0000-C000-000000000046");
            uint count = shellObjects.GetCount();

            for (uint i = 0; i < count; i++)
            {
                object   at = shellObjects.GetAt(i, ref riid);
                JumpItem jumpItemForShellObject = null;
                try
                {
                    jumpItemForShellObject = GetJumpItemForShellObject(at);
                }
                catch (Exception exception)
                {
                    if ((exception is NullReferenceException) || (exception is SEHException))
                    {
                        throw;
                    }
                }
                _ShellObjectPair pair = new _ShellObjectPair {
                    ShellObject = at,
                    JumpItem    = jumpItemForShellObject
                };
                list.Add(pair);
            }
            return(list);
        }
Example #2
0
        private void _BuildShellLists(out List<JumpItem> successList, out List<_RejectedJumpItemPair> rejectedList, out List<_ShellObjectPair> removedList)
        {
            // Declare these outside the try block so we can cleanup native resources in the _ShellObjectPairs.
            List<List<_ShellObjectPair>> categories = null;
            removedList = null;
            ICustomDestinationList destinationList = CLSID.CoCreateInstance<ICustomDestinationList>(CLSID.DestinationList);
            try
            {
                // Even though we're not exposing Shell's AppModelId concept, we'll still respect it
                // since it's easy to clients to p/invoke to set it for Application and Window, but not for JumpLists
                string appId = _RuntimeId;
                if (!string.IsNullOrEmpty(appId))
                {
                    destinationList.SetAppID(appId);
                }

                // The number ot items visible on a jump list is a user setting.  Shell doesn't reject items based on overflow.
                // We don't bother checking against it because the app can query the setting and manage overflow based on it
                // if they really care.  We'll happily add too many items with the hope that if the user changes the setting
                // items will be recovered from the overflow.
                uint slotsVisible;
                Guid removedIid = new Guid(IID.ObjectArray);
                var objectsRemoved = (IObjectArray)destinationList.BeginList(out slotsVisible, ref removedIid);

                // Keep track of the items that were previously removed by the user.
                // We don't want to pend any items that are contained in this list.
                // It's possible that this contains null JumpItems when we were unable to do the conversion.
                removedList = GenerateJumpItems(objectsRemoved);

                // Keep track of the items that have been successfully pended.
                // IMPORTANT: Ensure at the end of this that if the list is applied again that it would
                //     result in items being added to the JumpList in the same order.
                //     This doesn't mean that they'll be ordered the same as how the user added them
                //     (e.g. categories will be coalesced), but the categories should appear in the same order.
                //     Since when we call AddCategory we're doing it in reverse order, AddCategory augments
                //     the items in the list in reverse as well.  At the end the final list is reversed.
                successList = new List<JumpItem>(JumpItems.Count);

                // Keep track of the items that we couldn't pend, and why.
                rejectedList = new List<_RejectedJumpItemPair>(JumpItems.Count);

                // Need to group the JumpItems based on their categories.
                // The special "Tasks" category doesn't actually have a name and should be first so it's unconditionally added.
                categories = new List<List<_ShellObjectPair>>() { new List<_ShellObjectPair>() };

                // This is not thread-safe.
                // We're traversing the original list so we're vulnerable to another thread modifying it during the enumeration.
                foreach (var jumpItem in JumpItems)
                {
                    if (jumpItem == null)
                    {
                        // App added a null jump item?  Just go through the normal failure mechanisms.
                        rejectedList.Add(new _RejectedJumpItemPair { JumpItem = jumpItem, Reason = JumpItemRejectionReason.InvalidItem });
                        continue;
                    }

                    object shellObject = null;
                    try
                    {
                        shellObject = GetShellObjectForJumpItem(jumpItem);
                        // If for some reason we couldn't create the item add it to the rejected list.
                        if (shellObject == null)
                        {
                            rejectedList.Add(new _RejectedJumpItemPair { Reason = JumpItemRejectionReason.InvalidItem, JumpItem = jumpItem });
                            continue;
                        }

                        // Don't add this item if it's in the list of items previously removed by the user.
                        if (ListContainsShellObject(removedList, shellObject))
                        {
                            rejectedList.Add(new _RejectedJumpItemPair { Reason = JumpItemRejectionReason.RemovedByUser, JumpItem = jumpItem });
                            continue;
                        }

                        var shellMap = new _ShellObjectPair { JumpItem = jumpItem, ShellObject = shellObject };
                        if (string.IsNullOrEmpty(jumpItem.CustomCategory))
                        {
                            // No custom category, so add to the Tasks list.
                            categories[0].Add(shellMap);
                        }
                        else
                        {
                            // Find the appropriate category and add to that list.
                            // If it doesn't exist, add a new category for it.
                            bool categoryExists = false;
                            foreach (var list in categories)
                            {
                                // The first item in the category list can be used to check the name.
                                if (list.Count > 0 && list[0].JumpItem.CustomCategory == jumpItem.CustomCategory)
                                {
                                    list.Add(shellMap);
                                    categoryExists = true;
                                    break;
                                }
                            }
                            if (!categoryExists)
                            {
                                categories.Add(new List<_ShellObjectPair>() { shellMap });
                            }
                        }

                        // Shell interface is now owned by the category list.
                        shellObject = null;
                    }
                    finally
                    {
                        Utility.SafeRelease(ref shellObject);
                    }
                }

                // Jump List categories get added top-down, except for "Tasks" which is special and always at the bottom.
                // We want the Recent/Frequent to always be at the top so they get added first.
                // Logically the categories are added bottom-up, but their contents are top-down,
                // so we reverse the order we add the categories to the destinationList.
                // To preserve the item ordering AddCategory also adds items in reverse.
                // We need to reverse the final list when everything is done.
                categories.Reverse();

                if (ShowFrequentCategory)
                {
                    destinationList.AppendKnownCategory(KDC.FREQUENT);
                }

                if (ShowRecentCategory)
                {
                    destinationList.AppendKnownCategory(KDC.RECENT);
                }

                // Now that all the JumpItems are grouped add them to the custom destinations list.
                foreach (List<_ShellObjectPair> categoryList in categories)
                {
                    if (categoryList.Count > 0)
                    {
                        string categoryHeader = categoryList[0].JumpItem.CustomCategory;
                        AddCategory(destinationList, categoryHeader, categoryList, successList, rejectedList);
                    }
                }

                destinationList.CommitList();

                // Swap the current list with what we were able to successfully place into the JumpList.
                // Reverse it first to ensure that the items are in a repeatable order.
                successList.Reverse();
            }
            finally
            {
                // Deterministically release native resources.

                Utility.SafeRelease(ref destinationList);

                if (categories != null)
                {
                    foreach (List<_ShellObjectPair> list in categories)
                    {
                        _ShellObjectPair.ReleaseShellObjects(list);
                    }
                }

                // Note that this only clears the ShellObjects, not the JumpItems.
                // We still need the JumpItems out of this list for the JumpItemsRemovedByUser event.
                _ShellObjectPair.ReleaseShellObjects(removedList);
            }
        }
Example #3
0
        private void _BuildShellLists(out List <JumpItem> successList, out List <_RejectedJumpItemPair> rejectedList, out List <_ShellObjectPair> removedList)
        {
            List <List <_ShellObjectPair> > categories = null;

            removedList = null;
            var destinationList = CLSID.CoCreateInstance <ICustomDestinationList>(CLSID.DestinationList);

            try
            {
                var appId = _RuntimeId;
                if (!string.IsNullOrEmpty(appId))
                {
                    destinationList.SetAppID(appId);
                }

                uint slotsVisible;
                var  removedIid     = new Guid(IID.ObjectArray);
                var  objectsRemoved = (IObjectArray)destinationList.BeginList(out slotsVisible, ref removedIid);

                removedList = GenerateJumpItems(objectsRemoved);

                successList = new List <JumpItem>(JumpItems.Count);

                rejectedList = new List <_RejectedJumpItemPair>(JumpItems.Count);

                categories = new List <List <_ShellObjectPair> > {
                    new List <_ShellObjectPair>()
                };

                foreach (var jumpItem in JumpItems)
                {
                    if (jumpItem == null)
                    {
                        rejectedList.Add(new _RejectedJumpItemPair {
                            JumpItem = jumpItem, Reason = JumpItemRejectionReason.InvalidItem
                        });
                        continue;
                    }
                    object shellObject = null;
                    try
                    {
                        shellObject = GetShellObjectForJumpItem(jumpItem);

                        if (shellObject == null)
                        {
                            rejectedList.Add(new _RejectedJumpItemPair {
                                Reason = JumpItemRejectionReason.InvalidItem, JumpItem = jumpItem
                            });
                            continue;
                        }

                        if (ListContainsShellObject(removedList, shellObject))
                        {
                            rejectedList.Add(new _RejectedJumpItemPair {
                                Reason = JumpItemRejectionReason.RemovedByUser, JumpItem = jumpItem
                            });
                            continue;
                        }
                        var shellMap = new _ShellObjectPair {
                            JumpItem = jumpItem, ShellObject = shellObject
                        };
                        if (string.IsNullOrEmpty(jumpItem.CustomCategory))
                        {
                            categories[0].Add(shellMap);
                        }
                        else
                        {
                            var categoryExists = false;
                            foreach (var list in categories)
                            {
                                if (list.Count > 0 && list[0].JumpItem.CustomCategory == jumpItem.CustomCategory)
                                {
                                    list.Add(shellMap);
                                    categoryExists = true;
                                    break;
                                }
                            }
                            if (!categoryExists)
                            {
                                categories.Add(new List <_ShellObjectPair> {
                                    shellMap
                                });
                            }
                        }

                        shellObject = null;
                    }
                    finally
                    {
                        Utility.SafeRelease(ref shellObject);
                    }
                }

                categories.Reverse();
                if (ShowFrequentCategory)
                {
                    destinationList.AppendKnownCategory(KDC.FREQUENT);
                }
                if (ShowRecentCategory)
                {
                    destinationList.AppendKnownCategory(KDC.RECENT);
                }

                foreach (var categoryList in categories)
                {
                    if (categoryList.Count > 0)
                    {
                        var categoryHeader = categoryList[0].JumpItem.CustomCategory;
                        AddCategory(destinationList, categoryHeader, categoryList, successList, rejectedList);
                    }
                }
                destinationList.CommitList();

                successList.Reverse();
            }
            finally
            {
                Utility.SafeRelease(ref destinationList);
                if (categories != null)
                {
                    foreach (var list in categories)
                    {
                        _ShellObjectPair.ReleaseShellObjects(list);
                    }
                }

                _ShellObjectPair.ReleaseShellObjects(removedList);
            }
        }
Example #4
0
        private void ApplyList()
        {
            Debug.Assert(_initializing == false);
            Verify.IsApartmentState(ApartmentState.STA);

            // We don't want to force applications to conditionally check this before constructing a JumpList,
            // but if we're not on 7 then this isn't going to work.  Fail fast.
            if (!Utilities.IsOSWindows7OrNewer)
            {
                RejectEverything();
                return;
            }

            List <JumpItem> successList;
            List <_RejectedJumpItemPair> rejectedList;

            // Declare these outside the try block so we can cleanup native resources in the _ShellObjectPairs.
            List <List <_ShellObjectPair> > categories  = null;
            List <_ShellObjectPair>         removedList = null;
            var destinationList = (ICustomDestinationList)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid(CLSID.DestinationList)));

            try
            {
                // Even though we're not exposing Shell's AppModelId concept, we'll still respect it
                // since it's easy to clients to p/invoke to set it for Application and Window, but not for JumpLists
                string appId = _RuntimeId;
                if (!string.IsNullOrEmpty(appId))
                {
                    destinationList.SetAppID(appId);
                }

                // The number ot items visible on a jump list is a user setting.  Shell doesn't reject items based on overflow.
                // We don't bother checking against it because the app can query the setting and manage overflow based on it
                // if they really care.  We'll happily add too many items with the hope that if the user changes the setting
                // items will be recovered from the overflow.
                uint slotsVisible;
                Guid removedIid     = new Guid(IID.ObjectArray);
                var  objectsRemoved = (IObjectArray)destinationList.BeginList(out slotsVisible, ref removedIid);

                // Keep track of the items that were previously removed by the user.
                // We don't want to pend any items that are contained in this list.
                // It's possible that this contains null JumpItems when we were unable to do the conversion.
                removedList = GenerateJumpItems(objectsRemoved);

                // Keep track of the items that have been successfully pended.
                // IMPORTANT: Ensure at the end of this that if the list is applied again that it would
                //     result in items being added to the JumpList in the same order.
                //     This doesn't mean that they'll be ordered the same as how the user added them
                //     (e.g. categories will be coalesced), but the categories should appear in the same order.
                //     Since when we call AddCategory we're doing it in reverse order, AddCategory augments
                //     the items in the list in reverse as well.  At the end the final list is reversed.
                successList = new List <JumpItem>(JumpItems.Count);

                // Keep track of the items that we couldn't pend, and why.
                rejectedList = new List <_RejectedJumpItemPair>(JumpItems.Count);

                // Need to group the JumpItems based on their categories.
                // The special "Tasks" category doesn't actually have a name and should be first so it's unconditionally added.
                categories = new List <List <_ShellObjectPair> >()
                {
                    new List <_ShellObjectPair>()
                };

                // This is not thread-safe.
                // We're traversing the original list so we're vulnerable to another thread modifying it during the enumeration.
                foreach (var jumpItem in JumpItems)
                {
                    if (jumpItem == null)
                    {
                        // App added a null jump item?  Just go through the normal failure mechanisms.
                        rejectedList.Add(new _RejectedJumpItemPair {
                            JumpItem = jumpItem, Reason = JumpItemRejectionReason.InvalidItem
                        });
                        continue;
                    }

                    object shellObject = null;
                    try
                    {
                        shellObject = GetShellObjectForJumpItem(jumpItem);
                        // If for some reason we couldn't create the item add it to the rejected list.
                        if (shellObject == null)
                        {
                            rejectedList.Add(new _RejectedJumpItemPair {
                                Reason = JumpItemRejectionReason.InvalidItem, JumpItem = jumpItem
                            });
                            continue;
                        }

                        // Don't add this item if it's in the list of items previously removed by the user.
                        if (ListContainsShellObject(removedList, shellObject))
                        {
                            rejectedList.Add(new _RejectedJumpItemPair {
                                Reason = JumpItemRejectionReason.RemovedByUser, JumpItem = jumpItem
                            });
                            continue;
                        }

                        var shellMap = new _ShellObjectPair {
                            JumpItem = jumpItem, ShellObject = shellObject
                        };
                        if (string.IsNullOrEmpty(jumpItem.CustomCategory))
                        {
                            // No custom category, so add to the Tasks list.
                            categories[0].Add(shellMap);
                        }
                        else
                        {
                            // Find the appropriate category and add to that list.
                            // If it doesn't exist, add a new category for it.
                            bool categoryExists = false;
                            foreach (var list in categories)
                            {
                                // The first item in the category list can be used to check the name.
                                if (list.Count > 0 && list[0].JumpItem.CustomCategory == jumpItem.CustomCategory)
                                {
                                    list.Add(shellMap);
                                    categoryExists = true;
                                    break;
                                }
                            }
                            if (!categoryExists)
                            {
                                categories.Add(new List <_ShellObjectPair>()
                                {
                                    shellMap
                                });
                            }
                        }

                        // Shell interface is now owned by the category list.
                        shellObject = null;
                    }
                    finally
                    {
                        Utilities.SafeRelease(ref shellObject);
                    }
                }

                // Jump List categories get added top-down, except for "Tasks" which is special and always at the bottom.
                // We want the Recent/Frequent to always be at the top so they get added first.
                // Logically the categories are added bottom-up, but their contents are top-down,
                // so we reverse the order we add the categories to the destinationList.
                // To preserve the item ordering AddCategory also adds items in reverse.
                // We need to reverse the final list when everything is done.
                categories.Reverse();

                if (ShowFrequentCategory)
                {
                    destinationList.AppendKnownCategory(KDC.FREQUENT);
                }

                if (ShowRecentCategory)
                {
                    destinationList.AppendKnownCategory(KDC.RECENT);
                }

                // Now that all the JumpItems are grouped add them to the custom destinations list.
                foreach (List <_ShellObjectPair> categoryList in categories)
                {
                    if (categoryList.Count > 0)
                    {
                        string categoryHeader = categoryList[0].JumpItem.CustomCategory;
                        AddCategory(destinationList, categoryHeader, categoryList, successList, rejectedList);
                    }
                }

                destinationList.CommitList();
            }
            catch
            {
                // It's not okay to throw an exception here.  If Shell is rejecting the JumpList for some reason
                // we don't want to be responsible for the app throwing an exception in its startup path.
                // For common use patterns there isn't really any user code on the stack, so there isn't
                // an opportunity to catch this in the app.
                // We can instead handle this.

                // Try to notify the developer if they're hitting this.
                if (TraceShell.IsEnabled)
                {
                    TraceShell.Trace(TraceEventType.Error, TraceShell.RejectingJumpItemsBecauseCatastrophicFailure);
                }
                RejectEverything();
                return;
            }
            finally
            {
                // Deterministically release native resources.

                Utilities.SafeRelease(ref destinationList);

                if (categories != null)
                {
                    foreach (List <_ShellObjectPair> list in categories)
                    {
                        _ShellObjectPair.ReleaseShellObjects(list);
                    }
                }

                // Note that this only clears the ShellObjects, not the JumpItems.
                // We still need the JumpItems out of this list for the JumpItemsRemovedByUser event.
                _ShellObjectPair.ReleaseShellObjects(removedList);
            }

            // Swap the current list with what we were able to successfully place into the JumpList.
            // Reverse it first to ensure that the items are in a repeatable order.
            successList.Reverse();
            _jumpItems = successList;

            // Raise the events for rejected and removed.
            EventHandler <JumpItemsRejectedEventArgs> rejectedHandler = JumpItemsRejected;
            EventHandler <JumpItemsRemovedEventArgs>  removedHandler  = JumpItemsRemovedByUser;

            if (rejectedList.Count > 0 && rejectedHandler != null)
            {
                var items   = new List <JumpItem>(rejectedList.Count);
                var reasons = new List <JumpItemRejectionReason>(rejectedList.Count);

                foreach (_RejectedJumpItemPair rejectionPair in rejectedList)
                {
                    items.Add(rejectionPair.JumpItem);
                    reasons.Add(rejectionPair.Reason);
                }

                rejectedHandler(this, new JumpItemsRejectedEventArgs(items, reasons));
            }

            if (removedList.Count > 0 && removedHandler != null)
            {
                var items = new List <JumpItem>(removedList.Count);
                foreach (_ShellObjectPair shellMap in removedList)
                {
                    // It's possible that not every shell object could be converted to a JumpItem.
                    if (shellMap.JumpItem != null)
                    {
                        items.Add(shellMap.JumpItem);
                    }
                }

                if (items.Count > 0)
                {
                    removedHandler(this, new JumpItemsRemovedEventArgs(items));
                }
            }
        }
Example #5
0
        private void _BuildShellLists(out List <JumpItem> successList, out List <_RejectedJumpItemPair> rejectedList, out List <_ShellObjectPair> removedList)
        {
            List <List <_ShellObjectPair> > list = null;

            removedList = null;
            Standard.ICustomDestinationList cdl = Standard.CLSID.CoCreateInstance <Standard.ICustomDestinationList>("77f10cf0-3db5-4966-b520-b7c54fd35ed6");
            try
            {
                uint   num;
                string str = _RuntimeId;
                if (!string.IsNullOrEmpty(str))
                {
                    cdl.SetAppID(str);
                }
                Guid riid = new Guid("92CA9DCD-5622-4bba-A805-5E9F541BD8C9");
                Standard.IObjectArray shellObjects = (Standard.IObjectArray)cdl.BeginList(out num, ref riid);
                removedList  = GenerateJumpItems(shellObjects);
                successList  = new List <JumpItem>(this.JumpItems.Count);
                rejectedList = new List <_RejectedJumpItemPair>(this.JumpItems.Count);
                list         = new List <List <_ShellObjectPair> > {
                    new List <_ShellObjectPair>()
                };
                foreach (JumpItem item in this.JumpItems)
                {
                    if (item == null)
                    {
                        _RejectedJumpItemPair pair = new _RejectedJumpItemPair {
                            JumpItem = item,
                            Reason   = JumpItemRejectionReason.InvalidItem
                        };
                        rejectedList.Add(pair);
                    }
                    else
                    {
                        object shellObject = null;
                        try
                        {
                            shellObject = GetShellObjectForJumpItem(item);
                            if (shellObject == null)
                            {
                                _RejectedJumpItemPair pair2 = new _RejectedJumpItemPair {
                                    Reason   = JumpItemRejectionReason.InvalidItem,
                                    JumpItem = item
                                };
                                rejectedList.Add(pair2);
                            }
                            else if (ListContainsShellObject(removedList, shellObject))
                            {
                                _RejectedJumpItemPair pair3 = new _RejectedJumpItemPair {
                                    Reason   = JumpItemRejectionReason.RemovedByUser,
                                    JumpItem = item
                                };
                                rejectedList.Add(pair3);
                            }
                            else
                            {
                                _ShellObjectPair pair4 = new _ShellObjectPair {
                                    JumpItem    = item,
                                    ShellObject = shellObject
                                };
                                if (string.IsNullOrEmpty(item.CustomCategory))
                                {
                                    list[0].Add(pair4);
                                }
                                else
                                {
                                    bool flag = false;
                                    foreach (List <_ShellObjectPair> list3 in list)
                                    {
                                        if ((list3.Count > 0) && (list3[0].JumpItem.CustomCategory == item.CustomCategory))
                                        {
                                            list3.Add(pair4);
                                            flag = true;
                                            break;
                                        }
                                    }
                                    if (!flag)
                                    {
                                        list.Add(new List <_ShellObjectPair> {
                                            pair4
                                        });
                                    }
                                }
                                shellObject = null;
                            }
                        }
                        finally
                        {
                            Standard.Utility.SafeRelease <object>(ref shellObject);
                        }
                    }
                }
                list.Reverse();
                if (this.ShowFrequentCategory)
                {
                    cdl.AppendKnownCategory(Standard.KDC.FREQUENT);
                }
                if (this.ShowRecentCategory)
                {
                    cdl.AppendKnownCategory(Standard.KDC.RECENT);
                }
                foreach (List <_ShellObjectPair> list5 in list)
                {
                    if (list5.Count > 0)
                    {
                        string customCategory = list5[0].JumpItem.CustomCategory;
                        AddCategory(cdl, customCategory, list5, successList, rejectedList);
                    }
                }
                cdl.CommitList();
                successList.Reverse();
            }
            finally
            {
                Standard.Utility.SafeRelease <Standard.ICustomDestinationList>(ref cdl);
                if (list != null)
                {
                    foreach (List <_ShellObjectPair> list7 in list)
                    {
                        _ShellObjectPair.ReleaseShellObjects(list7);
                    }
                }
                _ShellObjectPair.ReleaseShellObjects(removedList);
            }
        }