【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.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/25635.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ 20新特性之Ranges

&#x1f4a1; 如果想阅读最新的文章&#xff0c;或者有技术问题需要交流和沟通&#xff0c;可搜索并关注微信公众号“希望睿智”。 为什么要引入Ranges 在C 20之前&#xff0c;我们处理容器和迭代器时&#xff0c;通常需要使用一系列的算法和复杂的迭代器操作。这不仅繁琐&…

java aio nio区别

Java AIO&#xff08;Asynchronous I/O&#xff09;和NIO&#xff08;Non-blocking I/O&#xff09;在并发模型、API和适用场景等方面有所不同。具体分析如下&#xff1a; 并发模型 AIO&#xff1a;AIO提供了异步非阻塞的IO操作&#xff0c;通过回调函数来通知IO操作的完成。在…

栈----7-9 括号匹配

给定一串字符&#xff0c;不超过100个字符&#xff0c;可能包括括号、数字、字母、标点符号、空格&#xff0c;编程检查这一串字符中的( ) ,[ ],{ }是否匹配。 输入格式: 输入在一行中给出一行字符串&#xff0c;不超过100个字符&#xff0c;可能包括括号、数字、字母、标点符…

量化投资分析平台 迅投 QMT(六)资产定价绕不过去的BSM模型

量化投资分析平台 迅投 QMT [迅投 QMT](https://www.xuntou.net/?user_code7NYs7O)我目前在使用什么是BSM模型CQF课程介绍模型的五个重要的假设模型公式 我们为啥要学&#xff08;知道&#xff09;这玩意儿呢&#xff1f;隐含波动率&#xff08;Implied Volatility&#xff09…

初阶 《函数》 4. 函数的调用

4. 函数的调用 4.1 传值调用 函数的形参和实参分别占有不同内存块&#xff0c;对形参的修改不会影响实参 4.2 传址调用 传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式 这种传参方式可以让函数和函数外边的变量建立起真正的联系&#xff0c;也就是…

Bat脚本专栏目录及索引

文章目录 注释关闭回显和打印pauseerrorlevel 判断刚才的命令是否执行成功%value% 和 !value!的区别脚本名或路径有中文if条件判断语句for 循环语句set 变量设置findstr 搜索指令tasklist 进程列表指令文件操作时间延迟简单应用 注释 REM:: 关闭回显和打印 &#xff1a;字符…

大数据领域的workload是什么意思?

什么是workload&#xff1f; 在大数据领域&#xff0c;"workload"指的是需要处理的数据集和对其执行的操作的组合。它描述了大数据系统需要执行的任务的类型和规模。 我们可以从以下几个维度来理解大数据领域的 workload&#xff1a; 数据的特征: 数据量 需要处…

Linux下创建软raid(磁盘阵列)

raid &#xff0d;&#xff0d;磁盘阵列 RAID分为软RAID和硬RAID 如果cpu比较空闲&#xff0c;并且I/O比较慢的话&#xff0c;那么就比较适合使用软RAID 基本级别 0 1 5 0级别&#xff1a; 至少有两块硬盘&#xff0c;条带化&#xff0c;即把磁盘分散开&#xff0c; 写性能 …

sam_out 脱发预测

解释 这段代码是一个用于预测掉发问题的GPT模型的训练脚本。代码首先读取了一个包含预测特征的csv数据文件&#xff0c;并将特征进行编码。然后将数据集分成训练集和测试集。接下来定义了模型的结构&#xff0c;优化器和损失函数。然后进行多轮训练&#xff0c;每一轮都使用批…

堆和栈(heap and stack)

1、堆&#xff1a;一块内存空间&#xff0c;可以从中分配一个小buffer&#xff0c;用完后再把它放回去。 2、栈&#xff1a;也是一块内存空间&#xff0c;cpu的sp寄存器指向它&#xff0c;它可以用于函数调用、局部变量、多任务系统里保存现场。 PUSH [r3-r6,lr]; #将r3到r6寄…

C++之pair总结及其在点云数据存储中举例

1、前言 pair是将2个数据组合成一组数据&#xff0c;当需要这样的需求时就可以使用pair。如当一个函数需要返回2个数据的时候&#xff0c;可以选择pair。 pair的实现是一个结构体&#xff0c;主要的两个成员变量是first、second 因为是使用struct不是class&#xff0c;所以可以…

26.多边形的判定

上海市计算机学会竞赛平台 | YACSYACS 是由上海市计算机学会于2019年发起的活动,旨在激发青少年对学习人工智能与算法设计的热情与兴趣,提升青少年科学素养,引导青少年投身创新发现和科研实践活动。https://www.iai.sh.cn/problem/499 题目描述 给定 𝑛n 个整数 𝑎1,𝑎…

base上海,数据科学,数据挖掘,数据分析等岗位求收留

裁员了&#xff0c;base上海&#xff0c;数据科学&#xff0c;数据挖掘&#xff0c;数据分析等岗位&#xff0c;期望30k~40k&#xff0c;求推荐求收留 1&#xff0c;6年数据算法工作&#xff0c;做过指标体系搭建&#xff0c;用户画像&#xff0c;货品定价&#xff0c;社区分析…

Nacos注册中心和配置中心

1 nacos简介 1.1nacos介绍 Nacos是阿里的一个开源产品&#xff0c;它是针对微服务架构中的服务发现、配置管理、服务治理的综合型解决方案。是微服务的注册中心和配置中心&#xff0c;相当于springcloudEureka和springconfig的集合。 Nacos 致力于帮助您发现、配置和管理微服务…

使用Leaflet-canvas-label进行个性化标注实践详解

目录 前言 一、leaflet-canvas-label属性 1、地图展示属性 2、Canvas文本标注属性 3、事件列表 二、属性设置实战 1、标注放大比例 2、字体颜色和方向偏移 3、标注文字透明色设置 4、标注显示层级 三、事件绑定 1、颜色改变 2、事件绑定解析 3、标记初始化的一个小…

28.找零

上海市计算机学会竞赛平台 | YACSYACS 是由上海市计算机学会于2019年发起的活动,旨在激发青少年对学习人工智能与算法设计的热情与兴趣,提升青少年科学素养,引导青少年投身创新发现和科研实践活动。https://www.iai.sh.cn/problem/744 题目描述 有一台自动售票机,每张票卖 …

一文详解大模型微调全流程

节前&#xff0c;我们星球组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、参加社招和校招面试的同学. 针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 汇总合集&…

【大学物理】波动光学:光的衍射

23.2 单缝的夫琅禾费衍射_哔哩哔哩_bilibili 1 光的衍射和惠更斯-菲涅尔原理 干涉vs衍射&#xff1a;干涉研究的是两个分立的子光源&#xff0c;衍射研究的是连续的子光源。 两位科学家用分解的思想&#xff0c;一个解决了方向一个解决了光强。 2 单缝的夫琅禾费衍射 夫琅禾…

【JAVASE】java语法(成员变量与局部变量的区别、赋值运算符中的易错点)

一&#xff1a;成员变量与局部变量的区别 区别 成员变量 局部变量 类中位置不同 …

MySQL之查询性能优化(十四)

查询性能优化 使用用户自定义变量 优化排名语句 使用用户自定义变量的一个特性是你可以在给一个变量赋值的同时使用这个变量&#xff0c;换句话说&#xff0c;用户自定义变量的赋值具有"左值"特性。下面的例子展示了如何使用变量来实现一个类似"行号(row num…