2013-08-13 2 views
5

В моем приложении WPF необходимо указать локализованные имена всех приложений Metro/WinRT, установленных для пользователя. Я создал репозиторий для хранения рабочего образца для кода представлено: https://github.com/luisrigoni/metro-apps-listПолучите локализованные дружественные имена для всех приложений winrt/metro, установленных из приложения WPF.

1) Использование PackageManager.FindPackagesForUser() метод

var userSecurityId = WindowsIdentity.GetCurrent().User.Value; 
var packages = packageManager.FindPackagesForUser(userSecurityId); 
foreach (var package in packages) 
    Debug.WriteLine(package.Id.Name); 
} 

// output: 
// Microsoft.BingFinance 
// Microsoft.BingMaps 
// Microsoft.BingSports 
// Microsoft.BingTravel 
// Microsoft.BingWeather 
// Microsoft.Bing 
// Microsoft.Camera 
// microsoft.microsoftskydrive 
// microsoft.windowscommunicationsapps 
// microsoft.windowsphotos 
// Microsoft.XboxLIVEGames 
// Microsoft.ZuneMusic 
// Microsoft.ZuneVideo 

Эти выходы не кажется слишком дружелюбно, чтобы показать пользователю ...

2) Чтение AppxManifest.xml каждого из этих приложений

var userSecurityId = WindowsIdentity.GetCurrent().User.Value; 
var packages = packageManager.FindPackagesForUser(userSecurityId); 
foreach (var package in packages) 
{ 
    var dir = package.InstalledLocation.Path; 
    var file = Path.Combine(dir, "AppxManifest.xml"); 
    var obj = SerializationExtensions.DeSerializeObject<Package>(file); 

    if (obj.Applications != null) 
    { 
     foreach (var application in obj.Applications) 
     { 
      Debug.WriteLine(application.VisualElements.DisplayName); 
     } 
    } 
} 

// output: 
// ms-resource:AppTitle 
// ms-resource:AppDisplayName 
// ms-resource:BingSports 
// ms-resource:AppTitle 
// ms-resource:AppTitle 
// ms-resource:app_name 
// ms-resource:manifestDisplayName 
// ms-resource:ShortProductName 
// ms-resource:mailAppTitle 
// ms-resource:chatAppTitle 
// ms-resource:///resources/residTitle 
// ms-resource:///strings/peopleAppName 
// ms-resource:///photo/residAppName 
// ms-resource:34150 
// ms-resource:33273 
// ms-resource:33270 

Определенно не дружит ...

Update 1) Увеличение выше пункта (2 ) с SHLoadIndirectString несильно (намек на Erik F)

[DllImport("shlwapi.dll", BestFitMapping = false, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = false, ThrowOnUnmappableChar = true)] 
private static extern int SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, int cchOutBuf, IntPtr ppvReserved); 

static internal string ExtractStringFromPRIFile(string pathToPRI, string resourceKey) 
{ 
    string sWin8ManifestString = string.Format("@{{{0}? {1}}}", pathToPRI, resourceKey); 
    var outBuff = new StringBuilder(1024); 
    int result = SHLoadIndirectString(sWin8ManifestString, outBuff, outBuff.Capacity, IntPtr.Zero); 
    return outBuff.ToString(); 
} 
[...] 
foreach (var application in obj.Applications) 
{ 
    Uri uri = new Uri(application.VisualElements.DisplayName); 
    var resourceKey = string.Format("ms-resource://{0}/resources/{1}", package.Id.Name, uri.Segments.Last()); 
    Debug.WriteLine(ExtractStringFromPRIFile("<path/to/pri>", resourceKey)); 
} 
[...] 

// output: 
// Finance 
// Maps 
// Sports 
// Travel 
// Weather 
// Bing 
// Camera 
// SkyDrive 
// Mail 
// Messaging 
// Calendar 
// People 
// Photos 
// Games 
// Music 
// Video 

Гораздо, гораздо лучше. У нас уже есть английские этикетки. Но как извлечь другие языковые ресурсы?

Я ожидаю получить тот же ярлык, который отображается на экране запуска для каждого приложения, что-то вроде «Finanças», «Esportes», «Clima», если мой язык pt-BR; «Финансы», «Спорт», «Погода», если мой язык в США.

[Q] Есть ли другой способ получить имена приложений? Может быть, native/Win32 (DISM API/...)? Можно загрузить файл .pri каждого приложения, чтобы получить локализованное имя?

Как уже говорилось, обновленный работающий образец здесь: https://github.com/luisrigoni/metro-apps-list

ответ

7

Использование SHLoadIndirectString, вы должны быть в состоянии построить полностью квалифицированное ссылку на имя пакета и идентификатор ресурса вида @ {PackageFullName ресурс-ID?}

Документально здесь:

http://msdn.microsoft.com/en-us/library/windows/desktop/bb759919(v=vs.85).aspx

Вам придется преобразовать строку манифеста в правильную форму. Он должен быть: ms-resource: // PackageName/Resources/Id

Имя_пакета - это имя, а не полное имя. Ресурсы не требуются строго, но они по умолчанию и обычно там. Я бы попытался найти ресурс без вставки ресурсов, а затем повторить попытку, если это не удастся.

Например, приложение камеры имеет "MS-ресурс: manifestDisplayName" в манифесте, так что сначала вы должны попробовать (*): @ {Microsoft.Camera_6.2.8376.0_x64__8wekyb3d8bbwe? мс-ресурс: //Microsoft.Camera/manifestAppDescription}

Когда это не удается, вставить "ресурсы" и попробуйте: @ {Microsoft.Camera_6.2.8376.0_x64__8wekyb3d8bbwe? мс-ресурс: // Microsoft.Камера/ресурсы/manifestAppDescription}

Это должно сработать. Вы захотите попробовать обе формы, потому что слепо вставка «ресурсов» приведет к расторжению приложений, таких как skydrive, связь и фотографии, которые вставляют первую часть пути напрямую.

Все еще немного боль, но лучше, чем демпинг и синтаксический анализ гигантских XML-файлов.

(*) «Microsoft.Camera_6.2.8376.0_x64__8wekyb3d8bbwe» взято из примера - вы, очевидно, захотите получить полное имя того, которое действительно присутствует в вашей системе.

+0

Эта функция отлично работала для извлечения ресурсов en-us. Как настроить для получения значений ресурсов других языков? –

+0

Этот тип ссылок всегда будет использовать ваши языки по умолчанию или переопределение основного языка приложения. Это означает, что вам нужно изменить добавление другого языка в передний план списка языков вашего профиля пользователя на панели управления. –

+0

Что делать, если я использую этот метод для приложения, которое установлено для другого пользователя, а у пользователя другой язык, чем у моего пользователя? (оба пользователя в той же системе), является 'SHLoadIndirectString()' гарантированным для работы в таком случае. (Я буду использовать SHLoadIndirectString() в строке ресурса, полученной от другого пользователя) –

3

Похоже, вы застряли с makepri.exe dump /if <prifile>.pri /of <outfile>.xml. Затем все, что вам нужно сделать, это разбор/десериализация файла XML.

+1

К сожалению, makepri.exe является инструментом для разработчиков и требует наличия Windows Kit SDK. Мы должны были бы включить его в нашу заявку, и это не вариант для нас. В любом случае, спасибо за ответ. –

+1

@DiogoMuller Если вы не определяете фактический формат файла PRI (который недокументирован), я боюсь, что это ваш единственный вариант на данный момент. Вы также можете попробовать разобрать инструмент makepri, чтобы заглянуть, как он читает/записывает файл PRI. Но это звучит неприятно. –

0

На самом деле, вы можете сделать лучше, чем makepri - проверить ResourceIndexer:

http://msdn.microsoft.com/en-us/library/windows/apps/windows.applicationmodel.resources.management.resourceindexer.aspx

Вы должны быть в состоянии дать IndexFileContentsAsync в ПОИ файл и получить обратно все из кандидатов ресурсов в файле , Вам придется собирать и переинтерпретировать их, но вы получите все возможные значения ресурсов.

Для Windows 8 приложений, по крайней мере.

Для приложений, которые используют пакеты ресурсов (представленные в Windows 8.1), ресурсы.pri в пакете содержат только значения по умолчанию. Чтобы получить ресурсы для любых других установленных языков (или масштабных факторов), вам также нужно будет индексировать файлы PRI из дополнительных пакетов ресурсов.

+0

Можете ли вы дать какой-либо пример кода, чтобы получить 'display name' с помощью этого API? Я могу получить информацию о других ресурсах, таких как изображения, встроенные данные и т. Д., Но не отображаемое имя. Я использую 'ResourceIndexer :: IndexFileContentsAsync()' –

1

В дополнении к тому, что Erik F сказанных выше вместе с обновленным вопросом Луиса Ригони (OP) вот дальнейшие советы:

  1. Я обнаружил, что путь к ИРПУ является лучшим решением, что дает имя пакета. Много раз SHLoadIndirectString не разрешает ресурс, когда задано только имя пакета. Путь к PRI - это место установки пакета + resources.pri. Пример: C: \ Program Files \ WindowsApps \ Microsoft.MicrosoftEdge_8wekyb3d8bbwe \ Resources.pri.

  2. VisualElements/DisplayName может содержать полный URL-адрес ресурса. Если это так, вам не нужно форматировать его, используя имя пакета и папку «resources», например ms-resource: // {0}/resources/{1}. Если DisplayName содержит имя пакета, вы можете предположить, что это полный URL-адрес.

  3. Как и Erik F, при удалении SHLoadIndirectString повторите попытку, не используя/resources/folder.

  4. Также иногда сама папка ресурсов будет частью VisualElements/DisplayName. Пример: ms-resource: /// MSWifiResources/AppDisplayName. Также обратите внимание на три ///. Да, вам придется позаботиться об этом. Вы просто должны принять MSWifiResources/AppDisplayName и суффикс для MS-ресурса: /// MSWifiResources/AppDisplayName

.

[DllImport("shlwapi.dll", BestFitMapping = false, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = false, ThrowOnUnmappableChar = true)] 
public static extern int SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, int cchOutBuf, IntPtr ppvReserved); 

//If VisualElements/DisplayName contains ms-resource: then call the below 
//function. identity is nothing but package name that can be retrieved from 
//Identity/Name element in AppxManifest.xml. 

private static string GetName(string installPath, string name, string identity) { 
    StringBuilder sb = new StringBuilder(); 
    int result; 

    //if name itself contains the package name then assume full url else 
    //format the resource url 

    var resourceKey = (name.ToLower().Contains(identity.ToLower())) ? name : string.Format("ms-resource://{0}/resources/{1}", identity, name.Split(':')[[1]].TrimStart('/')); 
    string source = string.Format("@{{{0}? {1}}}", Path.Combine(installPath, "resources.pri"), resourceKey); 
    result = SHLoadIndirectString(source, sb, -1, IntPtr.Zero); 

    if (result == 0) 
     return sb.ToString(); 

    //if the above fails then we try the url without /resources/ folder 
    //because some apps do not place the resources in that resources folder 

    resourceKey = string.Format("ms-resource://{0}/{1}", identity, name.Split(':')[[1]].TrimStart('/')); 
    source = string.Format("@{{{0}? {1}}}", Path.Combine(installPath, "resources.pri"), resourceKey); 
    result = SHLoadIndirectString(source, sb, -1, IntPtr.Zero); 

    if (result == 0) 
     return sb.ToString(); 
    return string.Empty; 
} 
+0

Является ли это исчерпывающим списком возможностей? –

+0

HKU может быть проанализирован для получения отображаемых имен для всех пользователей '(https://github.com/Wox-launcher/Wox/issues/198)'. Можем ли мы использовать эти отображаемые имена для 'SHLoadIndirectString()' на HKCU всех пользователей для получения отображаемых имен для всех пакетов? (HKCU будет поддеревом HKU, поэтому мы можем получить HKCU всех пользователей) –

+0

Извините. Я не могу ответить на это. Можешь попробовать. –

Смежные вопросы