Плитка начального экрана Windows 8 на основе ярлыка на рабочем столе

У меня есть рабочий код (C#, но это не важно), который добавляет плитку на начальный экран Windows 8 путем создания файла ярлыка [.lnk] в специальных каталогах. как %APPDATA%\Microsoft\Windows\Start Menu\Programs и %LOCALAPPDATA%\Microsoft\Windows\Application Shortcuts. Я искал и нашел несколько полезных инструментов, таких как lnk-parser и OblyTile, которые помогли прояснить некоторые аспекты связанных с этим странностей. Я также нашел много статей и вопросов, связанных с этой темой, но ни одна из них не отвечает моим конкретным потребностям.

Я использую IShellLink (COM interop) для создания ярлыка и IPropertyStore (тот же экземпляр объекта), чтобы добавить некоторые необходимые свойства в файл ссылок. Моя конкретная проблема и вопрос таковы:

Созданная плитка работает так, как ожидалось, почти во всех аспектах. Он запускает нужную команду, имеет заданные цвета фона и переднего плана и отображает связанный с ним значок (.png изображение 144x144). Проблема в том, что, несмотря на то, что в файл ссылки [System.ItemTypeText] встроена пользовательская строка метки, она просто отображает имя физического файла без расширения. Это не так ужасно, но я бы предпочел, чтобы Windows учитывала встроенное свойство, а не имя файла.

Что может отсутствовать в файле ссылок, что вызывает такое поведение, а не желаемое?


Бонус

Используя IPropertyStore, я столкнулся с несколькими так называемыми наборами свойств, которые представляют собой просто свойства, сгруппированные по их GUID. Меня особенно интересуют наборы {b725f130-47ef-101a-a5f1-02608c9eebac} {86d40b4d-9069-443c-819a-2a54090dccec} и {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} (последний содержит вездесущий System.AppUserModel.ID).

Дополнительный вопрос: не могли бы вы указать какой-либо ресурс, который документирует любой (или оба) из этих двух наборов свойств? Я имею в виду, что означает каждое свойство и как они интерпретируются Windows. Это может быть даже полезнее, чем основной вопрос. Неважно, официальный он или неофициальный.

Примечание

Использование OblyTile путем вызова его из командной строки не является вариантом в соответствии с требованиями. Согласно тому, что я заметил, мне может потребоваться добавить пользовательский файл resources.pri в родительский каталог целевого файла ссылки. Хотелось бы избежать этого, если это вообще возможно, потому что формат не документирован.

Образец кода

Класс ShellLink — это старая добрая и скучная оболочка для интерфейсов IShellLink и IPropertyStore COM. Он работает просто отлично, я полностью уверен, что проблема не в нем.

using System;
using System.IO;

namespace Shell32NET
{
    public static class Win8Tiles
    {
        #region Fields

        /// <summary>
        /// b725f130-47ef-101a-a5f102608c9eebac
        /// </summary>
        static readonly Guid ItemTypeGroup = new Guid(
            0xb725f130, 0x47ef, 0x101a, 0xa5, 0xf1, 0x02, 0x60, 0x8c, 0x9e, 0xeb, 0xac
            );

        /// <summary>
        /// b725f130-47ef-101a-a5f102608c9eebac, 4
        /// </summary>
        static readonly PropertyKey SystemItemTypeText = new PropertyKey(ItemTypeGroup, 4);

        /// <summary>
        /// 86d40b4d-9069-443c-819a2a54090dccec
        /// </summary>
        static readonly Guid TilePropertiesGroup = new Guid(
            0x86d40b4d, 0x9069, 0x443c, 0x81, 0x9a, 0x2a, 0x54, 0x09, 0x0d, 0xcc, 0xec
            );

        /// <summary>
        /// 86d40b4d-9069-443c-819a2a54090dccec, 2
        /// </summary>
        static readonly PropertyKey TileSmallImageLocation = new PropertyKey(TilePropertiesGroup, 2);

        /// <summary>
        /// 86d40b4d-9069-443c-819a2a54090dccec, 4
        /// </summary>
        static readonly PropertyKey TileBackgroundColor = new PropertyKey(TilePropertiesGroup, 4);

        /// <summary>
        /// 86d40b4d-9069-443c-819a2a54090dccec, 5
        /// </summary>
        static readonly PropertyKey TileForegroundColor = new PropertyKey(TilePropertiesGroup, 5);

        /// <summary>
        /// 86d40b4d-9069-443c-819a2a54090dccec, 11
        /// </summary>
        static readonly PropertyKey TileDisplayName = new PropertyKey(TilePropertiesGroup, 11);

        /// <summary>
        /// 86d40b4d-9069-443c-819a2a54090dccec, 12
        /// </summary>
        static readonly PropertyKey TileImageLocation = new PropertyKey(TilePropertiesGroup, 12);

        /// <summary>
        /// 86d40b4d-9069-443c-819a2a54090dccec, 14
        /// </summary>
        static readonly PropertyKey TileUnknownFlags = new PropertyKey(TilePropertiesGroup, 14);

        /// <summary>
        /// 9f4c2855-9f79-4b39-a8d0e1d42de1d5f3
        /// </summary>
        public static readonly Guid MetadataGroup = new Guid(
            0x9f4c2855, 0x9f79, 0x4b39, 0xa8, 0xd0, 0xe1, 0xd4, 0x2d, 0xe1, 0xd5, 0xf3
            );

        /// <summary>
        /// 9f4c2855-9f79-4b39-a8d0e1d42de1d5f3, 5
        /// </summary>
        public static readonly PropertyKey AppUserModelID = new PropertyKey(MetadataGroup, 5);

        /// <summary>
        /// b6578b39-11f9-449b-8438cb5cf03b7d9c
        /// </summary>
        static readonly Guid UnknownGroup1 = new Guid(
            0xb6578b39, 0x11f9, 0x449b, 0x84, 0x38, 0xcb, 0x5c, 0xf0, 0x3b, 0x7d, 0x9c
            );

        #endregion

        /// <summary>
        /// Creates a tile in the Windows 8 start screen.
        /// </summary>
        /// <param name="target">The file to be executed when the tile is clicked.</param>
        /// <param name="appId">The registering application's ID.</param>
        /// <param name="title">The caption text for the tile.</param>
        /// <param name="imageFilename">The image to be shown in the tile.</param>
        /// <param name="background">Background color for the tile.<para>
        /// Must be in the hex ARGB form: 0xAARRGGBB.
        /// Where AA is the alpha channel value, RR is for red, GG for green and BB for blue.
        /// </para></param>
        /// <param name="foreground">Foregreound color for the tile, in the
        /// same format as <paramref name="background"/>.</param>
        public static void CreateTile(
            string target,
            string appId,
            string title,
            string imageFilename,
            uint background,
            uint foreground
            )
        {
            string appdata = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
            if (appdata == null)
                throw new NotSupportedException("The user's roaming application data directory does not exist.");

            using (var link = new ShellLink())
            {
                link.TargetPath = target;

                // The .lnk icon location and index (not quite important). Just using some defaults.
                link.SetIconLocation(@"%SystemRoot%\System32\SHELL32.DLL", 135);

                link.SetProperty(AppUserModelID, appId);

                // Apparently required properties (AS-IS).
                link.SetProperty(new PropertyKey(MetadataGroup, 11), true);
                link.SetProperty(new PropertyKey(MetadataGroup, 19), "Microsoft.InternetExplorer.Default");
                link.SetProperty(
                    new PropertyKey(MetadataGroup, 20),
                    "-contentTile -formatVersion 0x00000002 -securityFlags 0x00000000 -url 0x00000057"
                    );

                /**
                 * These are not really working at the moment; the tile just shows the name of the .lnk file.
                 */
                link.SetProperty(SystemItemTypeText, title);
                link.SetProperty(TileDisplayName, title);

                // Background and foreground.
                link.SetProperty(TileBackgroundColor, background);
                link.SetProperty(TileForegroundColor, foreground);

                // Small and normal tile icon. Set your own.
                string fileUri = new Uri(imageFilename).AbsoluteUri;
                link.SetProperty(TileImageLocation, fileUri);
                link.SetProperty(TileSmallImageLocation, fileUri);

                // Apparently required, not the tiniest clue of why.
                link.SetProperty(TileUnknownFlags, 0x41u);
                link.SetProperty(new PropertyKey(UnknownGroup1, 4), 0x41u);

                // The file should be saved to %LOCALAPPDATA%\Microsoft\Windows\Application Shortcuts
                string filename = Path.Combine(appdata, @"Microsoft\Windows\Application Shortcuts");
                filename = Path.Combine(filename, "Win8Tiles-Test");
                Directory.CreateDirectory(filename);

                filename = Path.Combine(filename, title + ".lnk");
                link.Save(filename);
            }
        }
    }
}

person Leandro    schedule 04.06.2014    source источник
comment
Официальные свойства Microsoft задокументированы здесь: msdn.microsoft.com/en-us/library/windows/desktop/ Еще одним источником является файл propkey.h, доступный в Windows SDK. Пример онлайн здесь: code.google.com/p/audacity/source/browse/audacity-src/trunk/ Если у вас есть репродукция, мы можем попробовать чтобы помочь больше.   -  person Simon Mourier    schedule 05.06.2014
comment
Большое спасибо за комментарий, @SimonMournier. Я должен был упомянуть в вопросе, что пролистал (почти) весь MSDN. Мне также было известно о заголовочном файле propkey.h, хотя я заметил существенные различия между тем, на который вы указываете, и файлом из SDK на моем локальном диске. Я ошибся с первым GUID; Я обновляю вопрос. Я не могу найти нигде на Земле (в Интернете) что-либо об этих двух наборах, явно связанных с плитками Windows 8. Постараюсь поделиться репродукцией.   -  person Leandro    schedule 05.06.2014
comment
@SimonMourier: я добавил код воспроизведения. Вопрос становится довольно длинным, если вам нужно больше кода, просто дайте мне знать. Я бы поделился целым примером решения, но я не вижу, как (если это возможно) прикрепить файл к вопросу.   -  person Leandro    schedule 23.06.2014
comment
Можете ли вы также предоставить определения PropertyKey и ShellLink (вы можете использовать paste.bin)   -  person Simon Mourier    schedule 24.06.2014
comment
@SimonMourier: На самом деле там нет ничего особенно интересного. Ты можешь доверять мне. PropertyKey — это простая структура, содержащая GUID и целочисленный индекс, проходящий через конструктор, как видно из кода. ShellLink просто упаковывает вызовы вышеупомянутых интерфейсов COM. Там простой простой код. Я абсолютно уверен, что проблема не в этом резервном коде, а в чем-то, что отсутствует в свойствах, устанавливаемых в общем методе CreateTile(...).   -  person Leandro    schedule 24.06.2014
comment
Дело не в том, что мы вам не доверяем, просто у людей, которые хотят вам помочь, меньше работы. Обратитесь к этому: sscce.org Однако это повысит вероятность того, что люди будут помогать, и, следовательно, увеличит шанс найти решение.   -  person Simon Mourier    schedule 24.06.2014
comment
Я создал расширение оболочки ссылки, которое добавляет в систему новый тип файла ссылки. Когда оболочка запрашивает значение ключа TileDisplayName из моего расширения оболочки, я возвращаю строку My Test App Tile и вижу эту строку в заголовке плитки. Так что вы идете по правильному пути, но я не знаю, почему в вашем случае TileDisplayName не работает. Если вам интересно, я могу опубликовать все журналы связи между оболочкой и моим расширением оболочки.   -  person Denis Anisimov    schedule 24.06.2014
comment
@SimonMourier: я проверяю у своего работодателя возможность поделиться дополнительным (надеюсь, всем) кодом. Пожалуйста, не переключайтесь. Я прочитал по ссылке, которую вы дали. Вы предлагаете мне удалить комментарии из кода или просто поделиться большим количеством кода?   -  person Leandro    schedule 24.06.2014
comment
Просто то, что компилируется :)   -  person Simon Mourier    schedule 24.06.2014
comment
@DenisAnisimov: Спасибо за ваш комментарий и ваше предложение. Это интересный подход, что касается расширения оболочки. Эти журналы могут быть полезны, я не против покопаться в них. Как вы можете ими поделиться? Ссылка? Эл. адрес?   -  person Leandro    schedule 24.06.2014
comment
pastebin.com/Y6ZXBHcU   -  person Denis Anisimov    schedule 24.06.2014
comment
@DenisAnisimov: Решил! Большое спасибо! Ваш файл журнала действительно был полезен. Оказывается, я, видимо, пропустил что-то очевидное раньше. В журнале сразу появилось [{B725F130-47EF-101A-A5F1-02608C9EEBAC}, 10]. Каноническое имя: System.ItemNameDisplay. Это свойство я должен был использовать вместо System.ItemTypeText. Не знаю, откуда я это взял. В любом случае, большое спасибо. Из любопытства: как был сгенерирован этот файл журнала? Откуда берутся эти канонические имена?   -  person Leandro    schedule 24.06.2014
comment
@DenisAnisimov: Даже если вы не ответили на вопрос напрямую, возможно, вам следует добавить ответ, чтобы заработать очки репутации, потому что вы действительно предоставили средства для решающего прорыва. Просто говорю'.   -  person Leandro    schedule 24.06.2014
comment
Файл журнала был сгенерирован моей библиотекой Shell Ace. Как получить канонические имена вы можете найти в ответе.   -  person Denis Anisimov    schedule 24.06.2014


Ответы (1)


person    schedule
comment
Дельфы? Не знал об интерфейсе IPropertySystem, обязательно изучу. Спасибо за ваше время. - person Leandro; 25.06.2014