Charapter 1: 端倪 最近一直在用Pyglet做一個小的案例,但是實際運行起來時發現了嚴重的記憶體泄漏。經調查後發現平均每秒會爆出80-120不等的頁面錯誤。且可以觀察到記憶體正在不斷地以0.2-0.4mb不等的速度增長。 能夠複原此問題的代碼如下: # 導入庫 import pyglet w ...
傳統應用的快捷方式目標指向可執行文件的路徑,但是對於商店應用(也叫msix打包應用),則指向一個奇怪的字元串,使用IShellLink::GetPath
獲取路徑時,則得到的是空字元串,而我們的最終目的是要拿到應用的安裝路徑,那該怎麼辦呢?
首先解釋一下,那個奇怪的字元串叫AUMID(App User Model Id)
,由應用包系列名稱AppInfo.PackageFamilyName和應用標識符AppInfo.Id組成。
分3步獲取安裝目錄
1,先獲取快捷方式的PIDL
HRESULT hr = S_OK;
IShellLinkW* psl = NULL;
IPersistFile* psf = NULL;
LPITEMIDLIST pidlLnk = NULL;
do
{
hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, (LPVOID*)&psl);
if (FAILED(hr)) {
break;
}
hr = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&psf);
if (FAILED(hr)) {
break;
}
//載入快捷方式
hr = psf->Load(L"C:\\Users\\Xyy\\Desktop\\Microsoft Teams - 快捷方式.lnk", STGM_READ);
if (FAILED(hr)) {
break;
}
//獲取快捷方式
hr = psl->GetIDList(&pidlLnk);
if (FAILED(hr)) {
break;
}
} while (false);
//釋放資源
if (pidlLnk) ILFree(pidlLnk);
2,通過PIDL
獲取應用的AUMID
這裡要註意,並非所有拿不到路徑的快捷方式都是商店應用,因此要判斷快捷方式的父目錄是否是FOLDERID_AppsFolder
,這是一個虛擬目錄
...
LPITEMIDLIST pidlAppsFolder = NULL;
PWSTR ppszName = NULL;
do
{
...
//獲取FOLDERID_AppsFolder的PIDL
hr = SHGetKnownFolderIDList(FOLDERID_AppsFolder, 0, NULL, &pidlAppsFolder);
if (FAILED(hr)) {
break;
}
//判斷當前快捷方式的父目錄是否是FOLDERID_AppsFolder
if (!ILIsParent(pidlAppsFolder, pidlLnk, FALSE)) {
printf("此快捷方式不是商店應用");
break;
}
//根據PIDL獲取AUMID
hr = SHGetNameFromIDList(pidlLnk, SIGDN_PARENTRELATIVEPARSING, &ppszName);
if (FAILED(hr)) {
break;
}
} while (false);
//釋放資源
...
if (pidlAppsFolder) ILFree(pidlAppsFolder);
if (ppszName) CoTaskMemFree(ppszName);
3,通過AUMID
解析出packageFamily,再根據PackageManager解析出安裝目錄
PackageManager
是WinRT
的類型,如何在c++中使用WinRT,請參考C++/WinRT
以下代碼需要管理員許可權才能運行。
//根據AUMID拿到packageFamily
std::wstring fullString(ppszName);
size_t pos = fullString.find(L'!');
if (pos == std::wstring::npos) {
break;
}
std::wstring packageFamily = fullString.substr(0, pos);
std::wstring installPath = L"";
PackageManager packageManager;
//通過packageFamily查找所有包
auto packages = packageManager.FindPackages(packageFamily);
for (auto package : packages) {
auto listEnties = package.GetAppListEntries();
for (auto entry : listEnties) {
if (entry.AppUserModelId() == ppszName) {
installPath == package.InstalledPath();
break;
}
}
if (!installPath.empty()) {
break;
}
}
if (installPath.empty()) {
break;
}
//找到安裝目錄
printf("找到安裝目錄:%ls", installPath.c_str());