0%

解析快捷方式指向的路径(1)

简介

在做垃圾文件清理的时候,会涉及到快捷方式的解析,以确定其是否有效

工作原理

操作系统提供了解析快捷方式的API,其中新型的 msi 快捷方式,多见于Office的程序,其文件路径是
类似 msi:xxxx 的ID信息,可以使用 MsiGetShortcutTarget 进行解析。普通的快捷方式,可以使用
IShellLink 来进行解析,但是这种方法有缺陷,比如指向 C:\Program Files\a.exe 的快捷方式,
用64位的程序去解析是正确的,但是用32位的程序去解析,就会得到 C:\Program Files (x86)\a.exe
无论是否使用 Wow64DisableWow64FsRedirection 关闭重定向,都是这样,所以只能解析 lnk 数据格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include <windows.h>
#include <atlbase.h>
#include <atlstr.h>
// (MAX_PATH * 2)
#define LARGE_PATH 520
LSTATUS ParseLnkGetPath(_In_ CStringA csLnk, _Out_ CStringA &csPath)
{
csLnk.MakeLower(); // 转为小写
if (csLnk.GetLength() < 5) return ERROR_INVALID_PARAMETER;
if (csLnk.Right(4) != ".lnk") return ERROR_INVALID_PARAMETER;
// 获取新型的快捷方式路径(比如OFFICE的)
CHAR szTemp[LARGE_PATH] = { 0 };
CHAR szProductCode[39] = { 0 };
CHAR szFeatureId[MAX_FEATURE_CHARS + 1] = { 0 };
CHAR szComponentCode[39] = { 0 };
LSTATUS ret = MsiGetShortcutTargetA(
(PCSTR)csLnk, szProductCode, szFeatureId, szComponentCode);
if (ret == ERROR_SUCCESS)
{
DWORD dwSize = LARGE_PATH * sizeof(WCHAR);
// 未检测szTemp空间是否足够大
INSTALLSTATE state = MsiGetComponentPathA(
szProductCode, szComponentCode, szTemp, &dwSize);
if (state == INSTALLSTATE_LOCAL)
{
_strlwr_s(szTemp); // 转为小写
csPath = szTemp;
return ERROR_SUCCESS;
}
return ERROR_INVALID_PARAMETER;
}
// 读取快捷方式的内容
ret = ERROR_INVALID_PARAMETER;
HANDLE hFile = CreateFileA((PCSTR)csLnk, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) return ret;
// 按照特定格式解析内容
DWORD dwFlag = 0;
DWORD dwReadLen = 0;
if (!ReadFile(hFile, &dwFlag, sizeof(DWORD), &dwReadLen, NULL)) goto Last;
if (dwReadLen != sizeof(DWORD)) goto Last;
if (dwFlag != 0x0000004CUL) goto Last;

DWORD dwRet = SetFilePointer(hFile, 0x00000014L, NULL, FILE_BEGIN);
if (dwRet == INVALID_SET_FILE_POINTER) goto Last;

if (!ReadFile(hFile, &dwFlag, sizeof(DWORD), &dwReadLen, NULL)) goto Last;
if (dwReadLen != sizeof(DWORD)) goto Last;

dwRet = SetFilePointer(hFile, 0x0000004CL, NULL, FILE_BEGIN);
if (dwRet == INVALID_SET_FILE_POINTER) goto Last;

if (dwFlag & 0x00000001UL)
{
WORD wSize = 0;
if (!ReadFile(hFile, &wSize, sizeof(WORD), &dwReadLen, NULL)) goto Last;
if (dwReadLen != sizeof(WORD)) goto Last;

dwRet = SetFilePointer(hFile, (LONG)wSize, NULL, FILE_CURRENT);
if (dwRet == INVALID_SET_FILE_POINTER) goto Last;
}

dwRet = SetFilePointer(hFile, 0x00000010L, NULL, FILE_CURRENT);
if (dwRet == INVALID_SET_FILE_POINTER) goto Last;

if (!ReadFile(hFile, &dwFlag, sizeof(DWORD), &dwReadLen, NULL)) goto Last;
if (dwReadLen != sizeof(DWORD)) goto Last;

dwRet = SetFilePointer(hFile,
-0x00000010L - (LONG)sizeof(DWORD) + (LONG)dwFlag, NULL, FILE_CURRENT);
if (dwRet == INVALID_SET_FILE_POINTER) goto Last;

int i = 0;
char ch = 0;
memset(szTemp, 0, sizeof(szTemp));
do
{
if (!ReadFile(hFile, &ch, sizeof(char), &dwReadLen, NULL)) goto Last;
*(szTemp + i++) = ch; // 未检测szTemp空间是否足够大
} while (ch != '\0');

_strlwr_s(szTemp); // 小写
csPath = szTemp;
ret = ERROR_SUCCESS;
Last:
CloseHandle(hFile);
return ret;
}