2015-08-14 18 views
5

Próbuję uzyskać zrozumiałą "nazwę procesu" dla aplikacji systemu Windows 10. Obecnie, wszystkie z nich korzystać ApplicationFrameHost, więc myślałem, że mogę używać albo ModelId lub PackageName, ale wydaje się system Windows 10 Store Apps (próbowałem z Mail, Store i Edge) nie będzie działać z Package query APIJak uzyskać "Nazwę aplikacji" z hWnd dla aplikacji Windows Store Store (np. Edge)

Używanie kernel32.dll, GetApplicationUserModelId zwraca APPMODEL_ERROR_NO_APPLICATION i GetPackageId zwraca APPMODEL_ERROR_NO_PACKAGE.

Jak mogę uzyskać identyfikator aplikacji sklepu Windows 10, aby jednoznacznie zidentyfikować, powiedzmy, Edge, ale także inne aplikacje Windows Store Store?


Aktualizacja

Dostaję identyfikator procesu z (klamki) hWnd, więc myślę, że mój problem jest faktycznie jak uzyskać „prawdziwe” identyfikator procesu z uchwytem okna. Stamtąd użycie tych metod prawdopodobnie by działało.

+0

Może jest tu coś, czego mi brakowało: https://msdn.microsoft.com/en-us/library/windows/apps/br211377.aspx - Będę musiał dalej kopać ... –

+1

Być może zainteresuje Cię odpowiedzi na moje pytanie: http://stackoverflow.com/questions/32360149/name-of-process-for-active-window-in-windows-8-10 using EnumWindows lub API UIAutomation. –

+0

@TimBeaudet dzięki za referencję. To rozwiązanie działa tylko wtedy, gdy okno nie jest * zminimalizowane, co stanowi problem dla programu alt + tab :) Twoja technika działa dla nieminimowanych przypadków, więc pomaga przynajmniej w niektórych przypadkach użycia! Dzięki, choć nie jest to jeszcze całkowicie rozwiązane. –

Odpowiedz

0

Można użyć GetPackageId(), a następnie PackageFullNameFromId().

Np .:

HANDLE hProcess = OpenProcess(
    PROCESS_QUERY_LIMITED_INFORMATION, 
    false, 
    pe32.th32ProcessID); 

UINT32 bufferLength = 0; 

LONG result = GetPackageId(hProcess, &bufferLength, nullptr); 

BYTE* buffer = (PBYTE) malloc(bufferLength); 
result = GetPackageId(hProcess, &bufferLength, buffer); 

PACKAGE_ID* packageId = reinterpret_cast<PACKAGE_ID*>(buffer); 
wprintf(L"Name: %s\n", packageId->name); 
+0

To wydaje się nie działać. Nawet zrobienie czegoś tak prostego jak 'GetPackageId (hProcess, ref len, IntPtr.Zero)' da 'APPMODEL_ERROR_NO_PACKAGE' we wszystkich aplikacjach, które wypróbowałem (Windows 10) –

+0

Zauważ, że być może otrzymuję zły proces ... pamiętaj, że proces ładuję jest 'ApplicationFrameHost', więc może muszę znaleźć" rzeczywisty "proces? –

+0

Spróbuj uzyskać uchwyt procesu za pomocą 'GetProcessHandleFromHwnd', a następnie pobierz identyfikator procesu za pomocą' GetProcessId'.Możesz zweryfikować identyfikator procesu, który reprezentujesz działający proces, otwierając Menedżer zadań i patrząc na kolumnę PID na karcie Szczegóły. – kiewic

1

GetPackageFullName/FamilyName/Id (hprocess, ...) itd powrócić APPMODEL_ERROR_NO_PACKAGE jeśli proces ma tożsamość opakowania. Ditto GetApplicationUserModelId (hprocess ...) zwraca APPMODEL_ERROR_NO_APPLICATION, ponieważ podobnie proces nie ma tożsamości aplikacji.

Brzmi, jakbyś miał HWND dla procesu, który działa w imieniu aplikacji, ale nie jest aplikacją. Jest to dość powszechne - RuntimeBroker i inne procesy działają jako "aplikacje na komputery" (to jest proces bez tożsamości pakietu lub aplikacji) jako brokerzy, aby robić rzeczy dla procesów aplikacji, których nie mogą sami zrobić.

Na twoje pierwotne pytanie: "Otrzymuję identyfikator procesu z hWnd (uchwyt okna), więc myślę, że moim problemem jest w rzeczywistości, jak uzyskać" prawdziwy "identyfikator procesu z uchwytu okna" to jest zasadniczo wadliwe podejście. Masz pid z HWND, ale jeśli proces jest brokerem, może działać w imieniu wielu aplikacji - proces brokera nie ma tożsamości; wie * na żądanie/wywołanie WinRT API/etc kim jest jego rozmówca i dopasowuje swoją pracę do tej tożsamości. Nie możesz tego odkryć na poziomie procesu.

+2

Mówisz, że używam zasadniczo wadliwego podejścia; w przypadku aplikacji systemu Windows 10 wszystkie okna (główne okno Poczta, Kalendarz, Sklep i wszystkie okna Edge) są hostowane w "ApplicationFrameHost". Celem mojej aplikacji jest pokazanie wszystkich otwartych okien, a dla każdego z nich potrzebuję procesu i ikony. I widzę, że Windows jest rzeczywiście w stanie uzyskać te informacje, jeśli spróbujesz 'Win' i' Tab', a także uruchom Menedżera zadań, zobaczysz, że ikona jest rzeczywiście powiązana z oknem. Jak mogę osiągnąć ten sam rezultat? (Obejście?) –

-1

Przede wszystkim istnieje obiekt o nazwie AppUserModelID, jest to identyfikator okna używanego przez pasek zadań do grupowania okien. Ponieważ wszystkie okna WinRT pochodzą z tego samego procesu, ale nie są zgrupowane, oznacza to, że każda aplikacja ma własny UserModelID.

Aby pobrać UserModelID z HWND, można użyć metody z this answer.

#include "Propsys.h" 
#include <propkey.h> 

#pragma comment (lib, "Shell32.lib") 

//......... 

IPropertyStore* propStore; 

auto weatherWnd = FindWindow(L"ApplicationFrameWindow", L"Weather"); 
SHGetPropertyStoreForWindow(weatherWnd, IID_IPropertyStore, (void**)&propStore); 

PROPVARIANT prop; 
propStore->GetValue(PKEY_AppUserModel_ID, &prop); 

I prop będzie zawierać wartość LPWSTR = 0x00838f68 L"Microsoft.BingWeather_8wekyb3d8bbwe!App". Jest to pełna nazwa punktu wejścia w formacie <FullPackageFamilyName>!<EntryPoint>. Punkt wejścia dla uruchomionych aplikacji zwykle nazywany App. Punkty wejścia są zdefiniowane w manifeście aplikacji.

Interesująca sprawa - okno podrzędne, które należy do aplikacji, nie jest niszczone, ale zostaje przeniesione z hosta aplikacji do okna pulpitu.Nie wiem, dlaczego tak się dzieje, ale musisz być ostrożny, ponieważ FindWindow(nullptr, L"Weather") zwróciło okno aplikacji dla dzieci, a nie okno appframehost.

P.S. AppUserModelID to tylko ciąg znaków, a jego format nie jest udokumentowany, więc ta metoda nie jest dokładnie najbardziej niezawodna.

P.P.S. Również zauważyłem, że chcesz mieć ikonę i nazwę, można użyć PackageManager na to, że wymaga, aby odwołać winmd montaż, jak to zrobić, patrzeć here

+0

FYI "AppUserModelID" myląco istnieje na 2 sposoby: 1. Win7: Wynaleziony jako łańcuch zwisający poza HWND. Zarządzane przez User32. Używane przez glomming itp. (UI isms) 2. Win8: Zainwestowane jako ciąg * w tokenie procesu *. Zarządzane przez jądro (nie tylko coś z UI). Używany przez appmodel do wskazania, że ​​proces ma tożsamość aplikacji. –

+0

Procesy pakowania mają przypisany identyfikator ApplicationUserModelID * do ich tokena procesu w czasie tworzenia procesu. Jest to część ich DNA i nie można go zmienić, dlatego jest niezawodnie zaufana i używana przez aplikację do identyfikacji aplikacji. Umowa z trikiem Win7 User32, który jest tylko ciągiem związanym z oknem, więc znajduje się w pamięci trybu użytkownika; ponieważ jest on potencjalnie modyfikowalny, nie należy go używać do podejmowania decyzji dotyczących zaufania ani bezpieczeństwa. Stąd powodem, dla którego ** dodaliśmy AUMID do tokena procesu. Jest nawet patent oparty na tym *** :-) –

+0

* ApplicationUserModelID vel AppUserModelID inaczej AUIMD aka AppId –

1

UWP aplikacje są zawinięte w inna aplikacja/proces. Jeśli to się koncentruje, spróbuj znaleźć proces potomny UWP.

Będziesz potrzebował kilku metod P/Invoke. Spójrz na tej klasy, które zapewniają cały kod trzeba wykonać zadanie:

using System; 
using System.IO; 
using System.Runtime.InteropServices; 
using System.Text; 

namespace Stackoverflow 
{ 
    internal struct WINDOWINFO 
    { 
     public uint ownerpid; 
     public uint childpid; 
    } 

    public class UwpUtils 
    { 
     #region User32 
     [DllImport("user32.dll")] 
     public static extern IntPtr GetForegroundWindow(); 
     [DllImport("user32.dll", SetLastError = true)] 
     public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); 
     // When you don't want the ProcessId, use this overload and pass IntPtr.Zero for the second parameter 
     [DllImport("user32.dll", SetLastError = true)] 
     public static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId); 
     /// <summary> 
     /// Delegate for the EnumChildWindows method 
     /// </summary> 
     /// <param name="hWnd">Window handle</param> 
     /// <param name="parameter">Caller-defined variable; we use it for a pointer to our list</param> 
     /// <returns>True to continue enumerating, false to bail.</returns> 
     public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter); 

     [DllImport("user32", SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     public static extern bool EnumChildWindows(IntPtr hWndParent, EnumWindowProc lpEnumFunc, IntPtr lParam); 
     #endregion 

     #region Kernel32 
     public const UInt32 PROCESS_QUERY_INFORMATION = 0x400; 
     public const UInt32 PROCESS_VM_READ = 0x010; 

     [DllImport("kernel32.dll", SetLastError = true)] 
     public static extern bool QueryFullProcessImageName([In]IntPtr hProcess, [In]int dwFlags, [Out]StringBuilder lpExeName, ref int lpdwSize); 

     [DllImport("kernel32.dll", SetLastError = true)] 
     public static extern IntPtr OpenProcess(
      UInt32 dwDesiredAccess, 
      [MarshalAs(UnmanagedType.Bool)] 
      Boolean bInheritHandle, 
      Int32 dwProcessId 
     ); 
     #endregion 

     public static string GetProcessName(IntPtr hWnd) 
     { 
      string processName = null; 

      hWnd = GetForegroundWindow(); 

      if (hWnd == IntPtr.Zero) 
       return null; 

      uint pID; 
      GetWindowThreadProcessId(hWnd, out pID); 

      IntPtr proc; 
      if ((proc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, (int)pID)) == IntPtr.Zero) 
       return null; 

      int capacity = 2000; 
      StringBuilder sb = new StringBuilder(capacity); 
      QueryFullProcessImageName(proc, 0, sb, ref capacity); 

      processName = sb.ToString(0, capacity); 

      // UWP apps are wrapped in another app called, if this has focus then try and find the child UWP process 
      if (Path.GetFileName(processName).Equals("ApplicationFrameHost.exe")) 
      { 
       processName = UWP_AppName(hWnd, pID); 
      } 

      return processName; 
     } 

     #region Get UWP Application Name 

     /// <summary> 
     /// Find child process for uwp apps, edge, mail, etc. 
     /// </summary> 
     /// <param name="hWnd">hWnd</param> 
     /// <param name="pID">pID</param> 
     /// <returns>The application name of the UWP.</returns> 
     private static string UWP_AppName(IntPtr hWnd, uint pID) 
     { 
      WINDOWINFO windowinfo = new WINDOWINFO(); 
      windowinfo.ownerpid = pID; 
      windowinfo.childpid = windowinfo.ownerpid; 

      IntPtr pWindowinfo = Marshal.AllocHGlobal(Marshal.SizeOf(windowinfo)); 

      Marshal.StructureToPtr(windowinfo, pWindowinfo, false); 

      EnumWindowProc lpEnumFunc = new EnumWindowProc(EnumChildWindowsCallback); 
      EnumChildWindows(hWnd, lpEnumFunc, pWindowinfo); 

      windowinfo = (WINDOWINFO)Marshal.PtrToStructure(pWindowinfo, typeof(WINDOWINFO)); 

      IntPtr proc; 
      if ((proc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, (int)windowinfo.childpid)) == IntPtr.Zero) 
       return null; 

      int capacity = 2000; 
      StringBuilder sb = new StringBuilder(capacity); 
      QueryFullProcessImageName(proc, 0, sb, ref capacity); 

      Marshal.FreeHGlobal(pWindowinfo); 

      return sb.ToString(0, capacity); 
     } 

     /// <summary> 
     /// Callback for enumerating the child windows. 
     /// </summary> 
     /// <param name="hWnd">hWnd</param> 
     /// <param name="lParam">lParam</param> 
     /// <returns>always <c>true</c>.</returns> 
     private static bool EnumChildWindowsCallback(IntPtr hWnd, IntPtr lParam) 
     { 
      WINDOWINFO info = (WINDOWINFO)Marshal.PtrToStructure(lParam, typeof(WINDOWINFO)); 

      uint pID; 
      GetWindowThreadProcessId(hWnd, out pID); 

      if (pID != info.ownerpid) 
       info.childpid = pID; 

      Marshal.StructureToPtr(info, lParam, true); 

      return true; 
     } 
     #endregion 
    } 
} 

Teraz uzyskać uchwyt do bieżącego okna na pierwszym planie przy użyciu innego P/metody Invoke

[DllImport("user32.dll")] 
public static extern IntPtr GetForegroundWindow(); 

Użyj zwracaną wartość i wywołaj metodę GetProcessName z powyższego kodu. Powinieneś otrzymać poprawną nazwę/ścieżkę do procesu.

Oto prosty formularz, aby przetestować kod:

using System; 
using System.Runtime.InteropServices; 
using System.Windows.Forms; 
using StackOverflow; 

namespace Stackoverflow.Test 
{ 
    public partial class TestForm : Form 
    { 
     WinEventDelegate dele = null; 
     delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime); 

     [DllImport("user32.dll")] 
     static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags); 

     private const uint WINEVENT_OUTOFCONTEXT = 0; 
     private const uint EVENT_SYSTEM_FOREGROUND = 3; 

     [DllImport("user32.dll")] 
     public static extern IntPtr GetForegroundWindow(); 
     public TestForm() 
     { 
      InitializeComponent(); 

      dele = new WinEventDelegate(WinEventProc); 
      IntPtr m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, dele, 0, 0, WINEVENT_OUTOFCONTEXT); 
     } 

     public void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) 
     { 
      textBox1.AppendText(GetActiveWindowTitle() + "\n"); 
     } 

     private string GetActiveWindowTitle() 
     { 
      return UwpUtils.GetProcessName(GetForegroundWindow()); 
     } 
    } 
} 

Można pobrać pełny kod, w tym przykładzie/test na GitHub.

Powiązane problemy