导航

解析快捷方式指向的文件

Posted on 2018-10-29 20:25  talenth  阅读(1219)  评论(0编辑  收藏  举报

参考网址

  https://docs.microsoft.com/zh-cn/windows/desktop/api/msi/nf-msi-msigetshortcuttargetw

  https://docs.microsoft.com/zh-cn/windows/desktop/api/msi/nf-msi-msigetcomponentpathw

  还有n多从百度上搜索的网页

前言

  本文只针对用api方式解析指向本地其他文件的lnk文件, 其他的比如通过暴力解析lnk文件在此不再赘述

解析的核心逻辑

  目前, windows上共有两种格式的link文件, 因此针对不同的格式, 要用不同的解析api.

  一种是msi生成的格式, 要用到 MsiGetShortcutTarget 跟MsiGetShortcutTarget 这俩函数组合来进行解析; 另一种就是之前的需要通过 IShellLink接口来进行解析.

  按照msdn上的说明, 对于所有的链接文件, 要先使用第一种方法进行解析, 当解析失败并且链接文件存在的情况下, 再用第二种方法来进行解析

  代码如下(有些头文件可能不是必须的):

  1 #include <windows.h>
  2 #include <ShObjIdl.h>
  3 #include <Shlobj.h>
  4 #include <shlwapi.h>
  5 #include <pathcch.h>
  6 #include <msi.h>
  7 #include <io.h>
  8 #include <iostream>
  9 #include <string>
 10 #include <vector>
 11 #include <algorithm>
 12 #include <cctype>
 13 #include <set>
 14 
 15 #pragma comment(lib,"Shlwapi.lib")
 16 #pragma comment(lib,"Pathcch.lib")
 17 #pragma comment(lib,"Msi.lib")
 18 struct AppInfoInStartMenu_t
 19 {
 20     std::wstring name_;
 21     std::wstring ExeFullPath_;
 22     std::wstring path_;
 23 };
 24 bool ResolveShortcut(const std::wstring &linkFile, AppInfoInStartMenu_t &info)
 25 {
 26     static const DWORD pathMaxLen = MAX_PATH * 2;
 27     static wchar_t szTmp[pathMaxLen] = { 0 };
 28 
 29     if (linkFile.empty() || linkFile.size() >= pathMaxLen)
 30         return false;
 31 
 32     size_t strLen1 = (size_t)linkFile.size();
 33     memset(szTmp, 0, pathMaxLen * 2);
 34     memcpy(szTmp, linkFile.c_str(), strLen1 * 2);
 35     std::transform(szTmp, szTmp + strLen1, szTmp, ::tolower);
 36 
 37     wchar_t *ext = NULL;
 38     HRESULT hr = PathCchFindExtension(szTmp, strLen1 + 1, &ext);
 39     if (hr != S_OK || wcscmp(ext, L".lnk"))
 40         return false;
 41     const wchar_t *fileName = PathFindFileName(szTmp);
 42     if (fileName == szTmp// 没有找到文件名的指针 
 43         || fileName >= ext)
 44         return false;
 45     //link文件名
 46     info.name_ = std::wstring(linkFile.c_str() + (fileName - szTmp), ext - fileName);
 47 
 48     bool result = false;
 49     wchar_t productCodeStr[39] = { 0 };
 50     wchar_t szComponentCodeStr[39] = { 0 };
 51     // ret的值msdn上居然没有说明!!! 不过测试几个发现貌似就是System Error里面定义的, ERROR_SUCCESS means right;
 52     UINT ret = MsiGetShortcutTargetW(linkFile.c_str(), productCodeStr, NULL, szComponentCodeStr);
 53     if(ERROR_SUCCESS == ret)
 54     {
 55         //看了看msdn,INSTALLSTATE_SOURCE这个没看懂是什么意思, 剩下的就只有INSTALLSTATE_LOCAL 代表正确了
 56         // 下面这一段是动态申请的buffer, 频繁调用的的话,感觉容易造成内存碎片化
 57 //        DWORD bufSize = 0;
 58 //         INSTALLSTATE  ret2 = MsiGetComponentPathW(productCodeStr, szComponentCodeStr, NULL, &bufSize);
 59 //         if (INSTALLSTATE_LOCAL != ret2)
 60 //             return false;
 61 // 
 62 //         bufSize++;
 63 //         wchar_t *truePath = new wchar_t[bufSize];
 64 //         if (!truePath)
 65 //             return false;
 66 //         ret2 = MsiGetComponentPathW(productCodeStr, szComponentCodeStr, truePath, &bufSize);
 67 //         delete[] truePath;
 68 
 69         DWORD bufSize = pathMaxLen;
 70         memset(szTmp, 0, pathMaxLen * 2);
 71         INSTALLSTATE  ret2 = MsiGetComponentPathW(productCodeStr, szComponentCodeStr, szTmp, &bufSize);
 72         if (INSTALLSTATE_LOCAL != ret2)
 73             return false;
 74         wprintf(L"%s\n", szTmp);
 75         result = true;
 76     }
 77     else
 78     {
 79         IShellLinkW *psl = NULL;
 80         IPersistFile *ppf = NULL;
 81         do
 82         {
 83             HRESULT hr = S_FALSE;
 84             hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&psl));
 85             if (hr != S_OK)
 86                 break;
 87 
 88             hr = psl->QueryInterface(&ppf);
 89             if (hr != S_OK)
 90                 break;
 91 
 92             hr = ppf->Load(linkFile.c_str(), STGM_READ);
 93             if (hr != S_OK)
 94                 break;
 95 
 96             //指向的路径
 97             memset(szTmp, 0, pathMaxLen * 2);
 98             hr = psl->GetPath(szTmp, MAX_PATH, NULL, SLGP_RAWPATH);
 99             if (hr != S_OK)
100                 break;
101             result = true;
102         } while (false);
103         if (ppf)
104             ppf->Release();
105         if (psl)
106             psl->Release();
107     }
108 
109     //填充返回的其他数据
110     if (result)
111     {
112         result = false;
113         info.ExeFullPath_ = szTmp;
114         hr = PathCchRemoveFileSpec(szTmp, strLen1 + 1);
115         if (hr == S_OK)
116         {
117             info.path_ = szTmp;
118             result = true;
119         }
120     }
121     return result;
122 }