【Windows】UWP - Application Frame 窗口句柄溯源

2024-06-10 14:28

本文主要是介绍【Windows】UWP - Application Frame 窗口句柄溯源,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一、问题描述

二、解决方案

三、测试代码

参考文献


本文出处链接:[https://blog.csdn.net/qq_59075481/article/details/139574981]。

一、问题描述

当 GUI 线程的窗口属于 Windows/UWP 应用程序时,它们始终由进程 ApplicationFrameHost 托管。如果依然使用 Win32 的那套方法我们最终将获取到根进程 ApplicationFrameHost.exe 而不是实际的 AppContainer 进程或者 RuntimeBroker 进程。

例如下面的情况:

查看展开的 FrameWindow 结构

这里的 0xB1568 窗口作为根父窗口,由 ApplicationFrameHost.exe 进程创建,而它的子窗口中有一个窗口不是由 ApplicationFrameHost.exe 进程创建的,而是指向了它的实际的 App 包进程。

查阅:关于 ApplicationWindow、FrameWindow 和 CoreWindow 的信息。

二、解决方案

我们进行一个测试可以找到它。当将 Spy++ 的窗口查找光标拖动到系统设置界面的标题栏区域时,我们将成功捕获到 CoreWindow。

Spy++ 中捕获到 CoreWindow

 查看进程信息:

CoreWindow 对应实际进程信息

所以,优化 FrameWindow 溯源的方法也出来了,就是遍历子窗口,找到实际进程,然后再获取你想要的信息,比如获取它是否是 UWP 应用:GetApplicationUserModelId 或者 GetPackageFamilyName 函数。

GetApplicationUserModelId 示例:

#define _UNICODE 1
#define UNICODE 1#include <Windows.h>
#include <appmodel.h>
#include <malloc.h>
#include <stdlib.h>
#include <stdio.h>int ShowUsage();
void ShowProcessApplicationUserModelId(__in const UINT32 pid, __in HANDLE process);int ShowUsage()
{wprintf(L"Usage: GetApplicationUserModelId <pid> [<pid>...]\n");return 1;
}int __cdecl wmain(__in int argc, __in_ecount(argc) WCHAR * argv[])
{if (argc <= 1)return ShowUsage();for (int i=1; i<argc; ++i){UINT32 pid = wcstoul(argv[i], NULL, 10);if (pid > 0){HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);if (process == NULL)wprintf(L"Error %d in OpenProcess (pid=%u)\n", GetLastError(), pid);else{ShowProcessApplicationUserModelId(pid, process);CloseHandle(process);}}}return 0;
}void ShowProcessApplicationUserModelId(__in const UINT32 pid, __in HANDLE process)
{wprintf(L"Process %u (handle=%p)\n", pid, process);UINT32 length = 0;LONG rc = GetApplicationUserModelId(process, &length, NULL);if (rc != ERROR_INSUFFICIENT_BUFFER){if (rc == APPMODEL_ERROR_NO_APPLICATION)wprintf(L"Desktop application\n");elsewprintf(L"Error %d in GetApplicationUserModelId\n", rc);return;}PWSTR fullName = (PWSTR) malloc(length * sizeof(*fullName));if (fullName == NULL){wprintf(L"Error allocating memory\n");return;}rc = GetApplicationUserModelId(process, &length, fullName);if (rc != ERROR_SUCCESS)wprintf(L"Error %d retrieving ApplicationUserModelId\n", rc);elsewprintf(L"%s\n", fullName);free(fullName);
}

GetPackageFamilyName 示例:

#define _UNICODE 1
#define UNICODE 1#include <Windows.h>
#include <appmodel.h>
#include <malloc.h>
#include <stdlib.h>
#include <stdio.h>int ShowUsage();
void ShowProcessPackageFamilyName(__in const UINT32 pid, __in HANDLE process);int ShowUsage()
{wprintf(L"Usage: GetPackageFamilyName <pid> [<pid>...]\n");return 1;
}int __cdecl wmain(__in int argc, __in_ecount(argc) WCHAR * argv[])
{if (argc <= 1)return ShowUsage();for (int i=1; i<argc; ++i){UINT32 pid = wcstoul(argv[i], NULL, 10);if (pid > 0){HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);if (process == NULL)wprintf(L"Error %d in OpenProcess (pid=%u)\n", GetLastError(), pid);else{ShowProcessPackageFamilyName(pid, process);CloseHandle(process);}}}return 0;
}void ShowProcessPackageFamilyName(__in const UINT32 pid, __in HANDLE process)
{wprintf(L"Process %u (handle=%p)\n", pid, process);UINT32 length = 0;LONG rc = GetPackageFamilyName(process, &length, NULL);if (rc != ERROR_INSUFFICIENT_BUFFER){if (rc == APPMODEL_ERROR_NO_PACKAGE)wprintf(L"Process has no package identity\n");elsewprintf(L"Error %d in GetPackageFamilyName\n", rc);return;}PWSTR familyName = (PWSTR) malloc(length * sizeof(*familyName));if (familyName == NULL){wprintf(L"Error allocating memory\n");return;}rc = GetPackageFamilyName(process, &length, familyName);if (rc != ERROR_SUCCESS)wprintf(L"Error %d retrieving PackageFamilyName\n", rc);elsewprintf(L"%s\n", familyName);free(familyName);
}

三、测试代码

下面给出根据上文理论编写的,获取窗口进程的二进制文件路径的代码(C++):

#include <iostream>
#include <windows.h>
#include <psapi.h>
#include <string>
#include <Shlwapi.h>#pragma comment(lib, "Shlwapi.lib")struct SPYWINDOWINFO
{uint32_t ownerpid;uint32_t childpid;
};class UwpUtils
{
public:static std::wstring GetProcessName(HWND hWnd){std::wstring processName;if (hWnd == nullptr)return L"";uint32_t pID = 0;GetWindowThreadProcessId(hWnd, reinterpret_cast<LPDWORD>(&pID));HANDLE proc = nullptr;proc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pID);if (proc == nullptr)return L"";DWORD capacity = 2000;TCHAR exeName[2000];if (QueryFullProcessImageNameW(proc, 0, exeName, &capacity) == FALSE){CloseHandle(proc);return L"";}processName = std::wstring(exeName, capacity);if (!wcscmp(PathFindFileNameW(processName.c_str()), L"ApplicationFrameHost.exe")){std::cout << "ExtendedWindowMode: " << std::endl;processName = UWP_AppName(hWnd, pID);}else {std::cout << "LegacyWindowMode: " << std::endl;}return processName;}private:static std::wstring UWP_AppName(HWND hWnd, uint32_t pID){SPYWINDOWINFO windowinfo = { 0 };windowinfo.ownerpid = pID;windowinfo.childpid = windowinfo.ownerpid;LPVOID pWindowinfo = malloc(sizeof(windowinfo));if (pWindowinfo == nullptr) {return L"";}memcpy(pWindowinfo, &windowinfo, sizeof(windowinfo));WNDENUMPROC lpEnumFunc = EnumChildWindowsCallback;EnumChildWindows(hWnd, lpEnumFunc, reinterpret_cast<LPARAM>(pWindowinfo));memcpy(&windowinfo, pWindowinfo, sizeof(windowinfo));free(pWindowinfo);if (windowinfo.childpid <= 4) {return L"";}HANDLE proc = nullptr;proc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, windowinfo.childpid);if (proc == nullptr)return L"";std::cout << "AppContainer Real Process ID: " << windowinfo.childpid << std::endl;DWORD capacity = 2000;TCHAR exeName[2000];if (QueryFullProcessImageNameW(proc, 0, exeName, &capacity) == FALSE){CloseHandle(proc);return L"";}return std::wstring(exeName, capacity);}static BOOL CALLBACK EnumChildWindowsCallback(HWND hWnd, LPARAM lParam){SPYWINDOWINFO info;memcpy(&info, reinterpret_cast<LPCVOID>(lParam), sizeof(info));uint32_t pID = 0;GetWindowThreadProcessId(hWnd, reinterpret_cast<LPDWORD>(&pID));if (pID != info.ownerpid)info.childpid = pID;memcpy(reinterpret_cast<LPVOID>(lParam), &info, sizeof(info));return TRUE;}
};int main()
{// 测试代码:std::cout << "Please enter the handle to set the child window ";std::cout << "(hexadecimal input, example 0x12B4): ";std::string hexInput;std::cin >> hexInput;// 将十六进制字符串转换为数字HWND hwnd = reinterpret_cast<HWND>(std::stoull(hexInput, nullptr, 16));if (hwnd) {// 检查句柄是否有效if (IsWindow(hwnd)) {std::cout << "(Verified) Valid window handle: " << hwnd << std::endl;std::wstring processName = UwpUtils::GetProcessName(hwnd);std::wcout << "Process Name: " << processName << std::endl;}else {std::cerr << "(Verified) InValid window handle! " << std::endl;}}else {std::cerr << "Unable to convert input to a valid window handle! " << std::endl;}system("pause");return 0;
}

测试效果:

获取窗口进程信息的结果

参考文献

  • c++ - Name of process for active window in Windows 8/10 - Stack Overflow
  • How to get the "Application Name" from hWnd for Windows 10 Store Apps
  • stackoverflow-code-samples/src/Q32001621 (github.com)

本文出处链接:[https://blog.csdn.net/qq_59075481/article/details/139574981]。

本文发布于:2024.06.10.

这篇关于【Windows】UWP - Application Frame 窗口句柄溯源的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1048420

相关文章

问题-windows-VPN不正确关闭导致网页打不开

为什么会发生这类事情呢? 主要原因是关机之前vpn没有关掉导致的。 至于为什么没关掉vpn会导致网页打不开,我猜测是因为vpn建立的链接没被更改。 正确关掉vpn的时候,会把ip链接断掉,如果你不正确关掉,ip链接没有断掉,此时你vpn又是没启动的,没有域名解析,所以就打不开网站。 你可以在打不开网页的时候,把vpn打开,你会发现网络又可以登录了。 方法一 注意:方法一虽然方便,但是可能会有

Windows/macOS/Linux 安装 Redis 和 Redis Desktop Manager 可视化工具

本文所有安装都在macOS High Sierra 10.13.4进行,Windows安装相对容易些,Linux安装与macOS类似,文中会做区分讲解 1. Redis安装 1.下载Redis https://redis.io/download 把下载的源码更名为redis-4.0.9-source,我喜欢跟maven、Tomcat放在一起,就放到/Users/zhan/Documents

Windows中,.net framework 3.5安装

安装.net framework,目前已知2种方法,如下: 一、在MSDN下载对应的安装包,安装,这种可能无法安装成功,概率很大,不成功使用第二种方法,基本上没问题。 二、win8/8.1/10 下安装 .net framework 3.5.1: 1. 打开 win8/8.1/10 安装盘(这里指系统安装镜像文件),提取 sources\sxs 文件夹到 X:\sources\sxs (X代

Windows 可变刷新率是什么?如何开启?

在现代计算设备中,显示屏的刷新率对用户体验起着至关重要的作用。随着显示技术的不断进步,固定刷新率显示器逐渐被支持可变刷新率(Variable Refresh Rate, VRR)技术的显示器所取代。 可变刷新率定义 可变刷新率是什么?可变刷新率(VRR)是一种显示技术,它允许显示器的刷新率动态调整,以匹配显卡输出的帧率。传统的显示器通常具有固定的刷新率(如60Hz、75Hz等),这意味着显示器

如何设置windows计划任务

如何设置windows计划任务 前言:在工作过程中写了一个python脚本,用于调用jira接口查询bug单数量,想要在本地定时任务执行,每天发送到钉钉群提醒,写下操作步骤用于记录。 1. 准备 Python 脚本 确保你的 Python 脚本已经保存到一个文件,比如 jira_reminder.py。 2. 创建批处理文件 为了方便任务计划程序运行 Python 脚本,创建一个批处理文

【Java算法】滑动窗口 下

​ ​    🔥个人主页: 中草药 🔥专栏:【算法工作坊】算法实战揭秘 🦌一.水果成篮 题目链接:904.水果成篮 ​ 算法原理 算法原理是使用“滑动窗口”(Sliding Window)策略,结合哈希表(Map)来高效地统计窗口内不同水果的种类数量。以下是详细分析: 初始化:创建一个空的哈希表 map 用来存储每种水果的数量,初始化左右指针 left

获取Windows系统版本号(转)

https://blog.csdn.net/sunflover454/article/details/51525179

博通5720 windows server 2003 32位网卡驱动和系统

driver for DELL R320 Broadcom 5720 Windows 2003 32bit 本人安装windows server 2003 网卡驱动成功!! 提供方便网盘下载地址博通5720网卡驱动 : http://pan.baidu.com/s/1GQWpw  windows server 2003 ghost 系统: http://pan

Linux平台与Windows平台字节对齐(一)

记录时间:2014-10-20 20:31 今天白天在整理 winndows平台 + Arm平台的网络数据包。但期间由于经验不足,不了解两平台CPU架构的差距会影响到数据类型的大小,导致在工程非常紧急的时间里还浪费了很多宝贵的时间,鉴于问题的常见性 和 隐秘行,特此列出,以便能帮助需要帮助的人。 解决问题思路:在遇到网络交互数据不对时,可以从如下四方面去排除问题,通常只要使用这三种

Vue3的Teleport:Teleport是Vue3的一个新功能,它允许我们将子组件渲染到父组件以外的地方,这在处理模态框、弹出窗口等情况时非常有用

I. Teleport 的概述 Teleport 的定义:   在 Vue 3.0 中,Teleport 是一个新的内置组件,它允许我们将任何部分的渲染内容 Teleport(传送)到 Vue 应用范围之外的地方。 换句话说,你可以控制片段,让它们在 DOM 中的任何位置渲染,而不仅仅是在当前组件内部。   Teleport 的效用和应用场景:   Teleport 的主要用途是处理在 UI