自己实现快捷方式文件的创建

  最近写个程序需要创建快捷方式,可是烦人的杀软,每次都会拦截,尤其是程序使用的时候,要创建快捷方式就拦截,真是一肚子火啊。
找了很久的资料,终于找到了一篇关于快捷方式文件的数据结构分析的文章:

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是怎么分析得到快捷方式文件的这些数据结构的,是不是有什么工具?或者什么方法?

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值