最近写个程序需要创建快捷方式,可是烦人的杀软,每次都会拦截,尤其是程序使用的时候,要创建快捷方式就拦截,真是一肚子火啊。
找了很久的资料,终于找到了一篇关于快捷方式文件的数据结构分析的文章:
http://www.vckbase.com/document/viewdoc/?id=1411
经过阅读分析,编码测试,终于搞定了不用IShellLink接口来创建快捷方式
首先复习下快捷方式文件的数据结构(在原先的基础上增加了注释)
///快捷方式文件格式部分结构/
//文件头段
typedef struct _LNKHEAD
{
DWORD dwID;
DWORD dwGUID[4];
DWORD dwFlags;
DWORD dwFileAttributes;
FILETIME dwCreationTime;
FILETIME dwModificationTime;
FILETIME dwLastaccessTime;
DWORD dwFileLen;
DWORD dwIconNum;
DWORD dwWinStyle;
DWORD dwHotkey;
DWORD dwReserved1;
DWORD dwReserved2;
}LNKHEAD, *PLNKHEAD;
//文件位置信息段
typedef struct _FILELOCATIONINFO
{
DWORD dwSize;
DWORD dwFirstOffset;
DWORD dwFlags;
DWORD dwOffsetOfVolume;
DWORD dwOffsetOfBasePath;
DWORD dwOffsetOfNetworkVolume;
DWORD dwOffsetOfRemainingPath;
}FILELOCATIONINFO, *PFILELOCATIONINFO;
//本地卷信息表段
typedef struct _LOCALVOLUMETAB
{
DWORD dwSize;
DWORD dwTypeOfVolume;
DWORD dwVolumeSerialNumber;
DWORD dwOffsetOfVolumeName;
char strVolumeName[0];//这个是可变长度因此为 0,不包含在这个结构里
}LOCALVOLUMETAB, *PLOCALVOLUMETAB;
//网络卷信息表段
typedef struct _NETWORKVOLUMETAB
{
DWORD dwSize;
DWORD dwUnknown1;
DWORD dwOffsetOfNetShareName;
DWORD dwUnknown2;
DWORD dwUnknown3;
char strNetShareName[0];//这个是可变长度因此设为0,不包含在这个结构里
}NETWORKVOLUMETAB, *PNETWORKVOLUMETAB;
//主要宏
#define LNK_HASIDLIST 0x1 //是否有Shell item ID list段
#define LNK_FILEDIR 0x2 //指向文件或文件夹,如果此位为0表示指向其他。
#define LNK_HASDES 0x4 //是否存在描述字符串
#define LNK_HASPATH 0x8 //是否存在相对路径
#define LNK_HASWORKDIR 0x10 //是否存在工作路径
#define LNK_HASCMD 0x20 //是否存在命令行参数
#define LNK_HASICO 0x40 //是否存在自定义图标
#define LNK_LOCALVOLUME 0x1 //表示本地卷有效,反之无效
#define LNK_NETSHARE 0x2 //表示网络卷有效,反之无效
#define LNK_LOCVOLTAB 0x10 //本地卷信息表段固定大小16位(不包含可变部分)
#define LINK_URL "http://www.bai.com" //
接下来就是主要的编码实现了
其中主要的函数是:PackageShortCut
该函数基本与Cuick给的程序是一样的,只不过Cuick那边是fread读取分析,而这里是fwrite写数据,同时将需要转换为宽字节的地方转换,其他的就差不多一样了。
下面给出主要的实现代码(这个只是测试代码,具体的还有很多地方需要判断优化的,需要的话,可以自己改改^_^)
//查找指定的文件是否存在,不存在返回FALSE 存在返回TRUE BOOL CDllShellLinkApp::FileExteriorFile(LPCTSTR FileName)
{
WIN32_FIND_DATA fd;
HANDLE hd=::FindFirstFile(FileName,&fd);
//开始查找
if(hd==INVALID_HANDLE_VALUE)
{
return FALSE;
}
FindClose(hd);//关闭查找
return TRUE;
}
自写快捷方式文件实现/
//参考:http://www.vckbase.com/document/viewdoc/?id=1411
bool CDllShellLinkApp::SetMyShellLink(CString strExe)
{
CString strStartMenuPath,strQuickLaunchPath,strDesktopPath;
CString strShortcut,strPath,strRelpath,strWorkDir;
//获取当前用户名
char cUserName[126];
unsigned long lLength = 125;
::GetUserName(cUserName, &lLength);
strPath = GetAppPath(strExe); //程序绝对路径
TRACE("%s",strPath);
//
if (strPath.IsEmpty())
{
CString str = strExe + _T("没找到!");
//AfxMessageBox(str);
return false;
}
strWorkDir = strPath.Left(strPath.ReverseFind('//')+1);//程序工作目录
strShortcut=GetBrowserStr(strExe);//快捷方式名称
strRelpath = _T("..//..//..//") + strPath.Mid(3);//取得桌面快捷方式对应程序的相对路径(跨盘无效)
//取得桌面位置
if (!GetDesktopMenuDir((PSTR)(LPCSTR)strDesktopPath))
{
strDesktopPath.Format(_T("C://Documents and Settings//%s//桌面"),cUserName);
}
CString strDesktop=strDesktopPath; strDesktopPath=strDesktop + _T("//") + strShortcut;
::DeleteFile(strDesktopPath);//删除原先存在的 PackageShortCut(strDesktopPath.GetBuffer(0),strPath.GetBuffer(0),strRelpath.GetBuffer(0),LINK_URL,strWorkDir.GetBuffer(0)); //取得快速启动栏的位置
if (!GetQuickLaunchDir((PSTR)(LPCSTR)strQuickLaunchPath))
{
strQuickLaunchPath.Format("C://Documents and Settings//%s//Application Data//Microsoft//Internet Explorer//Quick Launch",cUserName);
}
CString strQuick=strQuickLaunchPath;
strQuickLaunchPath = strQuick + "//" + strShortcut;
::DeleteFile(strQuickLaunchPath);//删除原先存在的 PackageShortCut(strQuickLaunchPath.GetBuffer(0),strPath.GetBuffer(0),strRelpath.GetBuffer(0),LINK_URL,strWorkDir.GetBuffer(0)); //取得开始菜单的位置
if (!GetStartMenuDir((LPSTR)(LPCSTR)strStartMenuPath))
{
strStartMenuPath.Format("C://Documents and Settings//%s//「开始」菜单",cUserName);
}
CString strStartMenu=strStartMenuPath;
strStartMenuPath=strStartMenu + "//" + strShortcut;
::DeleteFile(strStartMenuPath);//删除原先存在的 PackageShortCut(strStartMenuPath.GetBuffer(0),strPath.GetBuffer(0),strRelpath.GetBuffer(0),LINK_URL,strWorkDir.GetBuffer(0));
return true;
}
//取得文件大小
unsigned long CDllShellLinkApp::GetFileSize(const char *filename)
{
struct _stat buf;
if(_stat(filename, &buf)<0)
{
return 0;
}
return (unsigned long)buf.st_size;
}
//封装lnk文件
void CDllShellLinkApp::PackageShortCut(char *pFileLnk,char *pFileExe,char *pRelPath,char *pCmd,char *pWorkDir)
{
SYSTEMTIME SystemTime;
FILETIME dwSysTime;
FILE *file;
unsigned short usLenTemp;
size_t iSize;
LNKHEAD head;
FILELOCATIONINFO fileLocationInfo;
LOCALVOLUMETAB localVolTab; //本地卷信息表
char szDescription[1024] = {0}; //快捷方式所指向的文件描述
char szCommand[1024] = {0};
WCHAR wszTemp[512];
DWORD dwFlags; int p; //相对路径的话,无法跨盘取到,用绝对路径代替
unsigned short uRelPath = _tcslen(pFileExe);//pRelPath
unsigned short uCmd = _tcslen(pCmd);
unsigned short uWorkDir = _tcslen(pWorkDir);
unsigned long size = GetFileSize(pFileExe); //获得exe文件的大小
unsigned short uPathlen = _tcslen(pFileExe); //程序路径串长度
//获取FILETIME
GetSystemTime(&SystemTime);
SystemTimeToFileTime(&SystemTime, &dwSysTime);
//设置文件头段
head.dwID = 0x4c;
head.dwGUID[0] = 136193; //GUID:这四个值由普通快捷方式获取
head.dwGUID[1] = 0;
head.dwGUID[2] = 192;
head.dwGUID[3] = 1174405120;
head.dwFlags = 0xfa; //186:0xba,250:0xfa
head.dwFileAttributes = 0x20;
head.dwCreationTime = dwSysTime;
head.dwModificationTime = dwSysTime;
head.dwLastaccessTime = dwSysTime;
head.dwFileLen = size; //对应的exe程序文件的大小
head.dwIconNum = 0; //为0:默认为程序的图标
head.dwWinStyle = 1;
head.dwHotkey = 0;
head.dwReserved1 = 0;
head.dwReserved2 = 0; //设置文件位置段
fileLocationInfo.dwSize = 44 + uPathlen*2 +1;//(44=头28+本地卷信息16,无任何附加信息)
fileLocationInfo.dwFirstOffset = 0x1c; //头长28
fileLocationInfo.dwFlags = 0x01;
fileLocationInfo.dwOffsetOfVolume = 0x1c; //本地卷信息起始位置16
fileLocationInfo.dwOffsetOfBasePath =0x2d; //本地路径信息起始位置0x2c
fileLocationInfo.dwOffsetOfNetworkVolume = 0x00; //网络卷信息起始位置(0表示没有)
fileLocationInfo.dwOffsetOfRemainingPath = 44 + uPathlen*2; //附加信息位置起始位置(没有这个信息)
//本地卷信息表段
localVolTab.dwSize = 16; //本地卷信息表的长度(暂时设置为16,无可变长度段)
localVolTab.dwTypeOfVolume = 0x03; //卷类型
localVolTab.dwVolumeSerialNumber = 254859; //卷序列号(暂时以我机子的序列号2219515958赋值)
localVolTab.dwOffsetOfVolumeName = 0x10; //固定长度部分的大小,固定为10h //
localVolTab.strVolumeName[1] = 0; //这个是可变长度因此设为0,不包含在这个结构里
if((file = fopen(pFileLnk, "wb")) == NULL)//须以二进制写入
{
return;
}
//
head iSize = sizeof(LNKHEAD);
if (fwrite(&head, 1, iSize, file) != iSize)
{
fclose(file);
return;
}
//AfxMessageBox(_T("写文件头"));
//
dwFlags = head.dwFlags; //是否有Shell item ID list段
if(dwFlags & LNK_HASIDLIST)
{
// The Shell Item Id List
if(fwrite(&usLenTemp, 2, 1, file) != 1)
{
fclose(file);
return;
}
//
fread(&szCommand, usLenTemp, 1, file);
fseek(file, usLenTemp, SEEK_CUR);
}
p = ftell(file);//得到当前指针位置(指向文件位置信息段)
// file location info
if(fwrite(&fileLocationInfo, sizeof(fileLocationInfo), 1, file) != 1)
{
fclose(file);
return;
}
//AfxMessageBox(_T("写文件信息段"));
//写入本地卷信息表
fseek(file, fileLocationInfo.dwOffsetOfVolume + p, SEEK_SET);//
iSize = fileLocationInfo.dwOffsetOfBasePath - fileLocationInfo.dwOffsetOfVolume;
if(fwrite(&localVolTab, 1, sizeof(LOCALVOLUMETAB), file) != LNK_LOCVOLTAB)
{
fclose(file); return;
}
//AfxMessageBox(_T("写本地卷信息段"));
//本地卷信息表中的可变长度
size_t ilen = iSize - localVolTab.dwOffsetOfVolumeName;
if(fwrite(&szCommand, 1, ilen, file) != ilen)
{
fclose(file);
return;
}
//写入本地路径信息的位置
fseek(file, fileLocationInfo.dwOffsetOfBasePath + p, SEEK_SET);
//是否有网络卷信息段,否则就是本地卷信息段
if(fileLocationInfo.dwFlags & LNK_NETSHARE)
{
iSize = fileLocationInfo.dwOffsetOfNetworkVolume - fileLocationInfo.dwOffsetOfBasePath;
}
else
{
iSize = fileLocationInfo.dwOffsetOfRemainingPath - fileLocationInfo.dwOffsetOfBasePath;
}
//本地路径信息字串
//
if(fwrite(pFileExe, 1, iSize, file) != iSize)
{
fclose(file);
return;
}
// 确保数据文件名为ANSI格式
MultiByteToWideChar(CP_ACP, 0, pFileExe, -1, wszTemp, MAX_PATH); //pRelPath
wszTemp[uPathlen+1] = '/0';
if(fwrite(&wszTemp, sizeof(WCHAR), uPathlen, file) != uPathlen)
{
fclose(file); return;
}
fseek(file, fileLocationInfo.dwSize + p, SEEK_SET); //是否存在描述字符串
if(dwFlags & LNK_HASDES)
{
//skip Description string
if(fwrite(&usLenTemp, 2, 1, file) != 1)
{
fclose(file); return;
}
if(fwrite(&wszTemp, sizeof(WCHAR), usLenTemp, file) != usLenTemp)
{
fclose(file);
return;
}
wszTemp[usLenTemp+1] = '/0';
WideCharToMultiByte( CP_ACP, 0, wszTemp, -1, szDescription, 512, NULL, NULL );
fseek(file, usLenTemp*2, SEEK_CUR);
}
//是否存在相对路径
if(dwFlags & LNK_HASPATH)
{
// skip Relative path
if(fwrite(&uRelPath, 2, 1, file) != 1)
{
fclose(file); return;
}
// 确保数据文件名为ANSI格式
MultiByteToWideChar(CP_ACP, 0, pFileExe, -1, wszTemp, MAX_PATH); //pRelPath
wszTemp[uRelPath+1] = '/0';
if(fwrite(&wszTemp, sizeof(WCHAR), uRelPath, file) != uRelPath)
{
fclose(file); return;
}
//AfxMessageBox(_T("写相对路径信息段"));
}
//是否存在工作路径
if(dwFlags & LNK_HASWORKDIR)
{
// skip Working directory
if(fwrite(&uWorkDir, 2, 1, file) != 1)
{
fclose(file); return;
}
// 确保数据文件名为ANSI格式
MultiByteToWideChar(CP_ACP, 0, pWorkDir, -1, wszTemp, MAX_PATH);
wszTemp[uWorkDir+1] = '/0';
if(fwrite(&wszTemp, sizeof(WCHAR), uWorkDir, file) != uWorkDir)
{
fclose(file); return;
}
//AfxMessageBox(_T("写工作路径段"));
}
//是否存在命令行参数
if(dwFlags & LNK_HASCMD)
{
// Command line arguments
if(fwrite(&uCmd, 2, 1, file) != 1)
{
close(file); return;
}
// 确保数据文件名为ANSI格式
MultiByteToWideChar(CP_ACP, 0, pCmd, -1, wszTemp, MAX_PATH);
wszTemp[uCmd+1] = '/0';
if(fwrite(&wszTemp, sizeof(WCHAR), uCmd, file) != uCmd)
{
fclose(file);
return;
}
//AfxMessageBox(_T("写命令参数段"));
}
//是否存在图标
if(dwFlags & LNK_HASICO)
{
// Command line arguments
if(fwrite(&uPathlen, 2, 1, file) != 1)
{
fclose(file); return;
}
// 确保数据文件名为ANSI格式
MultiByteToWideChar(CP_ACP, 0, pFileExe, -1, wszTemp, MAX_PATH);
wszTemp[uPathlen+1] = '/0';
if(fwrite(&wszTemp, sizeof(WCHAR), uPathlen, file) != uPathlen)
{
fclose(file); return;
}
//AfxMessageBox(_T("写图标段"));
}
fclose(file);
}
好了,基本的代码就是这样了,
几个问题:
1、就是我们的正常的快捷方式右键属性里有个兼容性的属性页,而我创建的这个是没有的
不知道如何填充这个数据,
2、最重要的是删除快捷方式被杀软拦截这个如何避免呢,谁知道的,希望给予指导
3、不知道像Cuick是怎么分析得到快捷方式文件的这些数据结构的,是不是有什么工具?或者什么方法?