解析快捷方式指向的路径

发布时间 2023-06-12 11:31:38作者: Jianrung

简介

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

工作原理

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

优化代码实现

#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;
}

转载自:解析快捷方式指向的路径(1)