参考网址
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 ", 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 }