通过手工操作建立这些菜单项或快捷方式并不复杂,在一般Windows使用手册中都有介绍,相信大家都很熟悉,在此不再赘述。笔者在有关资料的基础上,通过实践摸索,找到了在应用程序中完成上述工作的方法。这正是开发安装程序所必需的。
  Windows的快捷方式实际上是一个带有扩展名LNK的数据文件,其中包含有用于访问Windows某一对象(即在资源管理器中所能浏览的所有对象,包括文件,文件夹,驱动器及打印机等)的有关信息,如目标对象的路径和名称,工作目录,要传递的命令行参数,运行时的初始显示状态,图标及其快捷键等。通过在快捷方式上单击鼠标右键并在弹出菜单中选择“属性”可以观察该快捷方式的这些性质。
  快捷方式的数据文件如果存放在C:/Windows/Desktop子目录下,这个快捷方式就会显示在桌面上,而如果存放在C:/Windows/”Start Menu”/Programs子目录下,这个快捷方式就会作为“开始”菜单的一个菜单项出现。而桌面上的文件夹和“开始”菜单的菜单组则是上述两个子目录下的子目录的表现。
  Windows外壳(Shell)的快捷方式是以OLE技术的组件对象模型COM(Component ObjectModal)为基础而设计的。利用COM模型,一个应用程序可以调用另一应用程序的某些功能。这方面的技术细节请参阅有关文献。
  创建Windows的快捷方式比较容易。首先利用OLE通过调用CoCreateInstance()函数建立一个IID_IShellLink实例,并同时得到其接口指针。利用这个接口指针可以对其各项属性进行设置。为了使这些信息以快捷方式的数据文件(*.lnk)格式保存起来,还需要从IID_IShellLink对象取得其IID_IPersistFile接口指针,以便于调用其成员函数Save()保存前面设置的信息。
  至于如何删除快捷方式以及创建和删除文件夹,则只需要简单地调用文件操作函数SHFileOperation()就可以了。另外应该注意,在完成上述操作之后,都要调用SHChangeNotify()函数通知Windows外壳有关变化以使之及时更新其显示状态。
  该示例程序为一个基于对话框的应用程序,两个圆形按钮用于设置要创建/删除的文件夹或快捷方式的位置,下面的四个按钮则用于执行不同的操作。另外,该程序还需要一个简单的对话框,用于输入要创建的文件夹或快捷方式的名称。

// SortCut.cpp :
BOOL CSortCutApp::InitInstance()
{
    ......
    CoInitialize (NULL);
    CSortCutDlg dlg;
    m_pMainWnd = &dlg;
    ......
    CoUninitialize ();
    return FALSE;
}
// SortCutDlg.cpp :
#include "stdafx.h"
#include "SortCut.h"
#include "SortCutDlg.h"

#include "NameDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//起始文件夹的PIDL
int nBeginAt=CSIDL_DESKTOPDIRECTORY;
......

//浏览文件夹
BOOL BrowseForFolder(
LPITEMIDLIST pidlRoot,//浏览开始处的PIDL
LPITEMIDLIST *ppidlDestination,
//浏览结束时所选择的PIDL
LPCSTR lpszTitle)//浏览对话框中的提示文字
{ 
    BROWSEINFO BrInfo ;
    ZeroMemory( &BrInfo, sizeof(BrInfo)) ;
    BrInfo.hwndOwner = HWND_DESKTOP ;
    BrInfo.pidlRoot = pidlRoot ;
    BrInfo.lpszTitle = lpszTitle ;
   
    //浏览文件夹
    *ppidlDestination= SHBrowseForFolder(&BrInfo);

    //用户选择了取消按钮
    if(NULL == *ppidlDestination) return FALSE ;

    return TRUE ;
}
//取得快捷方式的目标应用程序名
SelectMenuItem( LPSTR szFileName)
{
    OPENFILENAME ofn ;
    static CHAR szFilter[] = "Programs/0*.exe/0" ;
    ZeroMemory(&ofn, sizeof( OPENFILENAME)) ;
    ofn.lStructSize = sizeof( OPENFILENAME) ;
    ofn.hwndOwner = HWND_DESKTOP;
    ofn.lpstrFilter = szFilter ;
    ofn.nFilterIndex = 0 ;
    ofn.nMaxFile = MAX_PATH ;
    ofn.lpstrTitle = "请选择目标应用程序:" ;
    ofn.lpstrFile = szFileName ;
    ofn.Flags = OFN_FILEMUSTEXIST |
    OFN_PATHMUSTEXIST | OFN_EXPLORER ;
   
    //文件浏览
    if(!GetOpenFileName( &ofn))//选择了取消按钮
       return FALSE ;
   
    return TRUE ;
}

//取得要创建的快捷方式的名字
BOOL GetShortcutCrt(LPSTR szPath)
{
    LPITEMIDLIST pidlBeginAt, pidlDestination ;

    // 取得开始菜单或桌面的PIDL
    SHGetSpecialFolderLocation( HWND_DESKTOP,
    nBeginAt, &pidlBeginAt) ;

    // 取得要创建的快捷方式所在的位置
    if( !BrowseForFolder(pidlBeginAt,&pidlDestination,"请选择快捷方式所在的位置:"))
       return FALSE ;

    // 把PIDL转换为路径名
    SHGetPathFromIDList( pidlDestination, szPath) ;

    // 取得快捷方式名称
    CNameDlg name_dlg;
    if(name_dlg.DoModal() == IDCANCEL)
       return FALSE ;

    //把快捷方式名和扩展名.LNK添加到路径名后
    //形成完整的快捷方式数据文件名
    wsprintf(szPath+lstrlen(szPath),"//%s.lnk",
    name_dlg.m_strName) ;
    return TRUE ;
}

//创建快捷方式
BOOL CreateLink (
LPSTR szPath,//快捷方式的目标应用程序名
LPSTR szLink)//快捷方式的数据文件名(*.lnk)
{
    HRESULT hres ;
    IShellLink * psl ;
    IPersistFile* ppf ;
    WORD wsz[ MAX_PATH] ;

    //创建一个IShellLink实例
    hres = CoCreateInstance( CLSID_ShellLink, NULL,    CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
    if( FAILED( hres))    return FALSE ;

    //设置目标应用程序
    psl -> SetPath( szPath) ;

    //设置快捷键(此处设为Shift+Ctrl+'R')
    psl -> SetHotkey( MAKEWORD( 'R',
    HOTKEYF_SHIFT |HOTKEYF_CONTROL)) ;

    //从IShellLink获取其IPersistFile接口
    //用于保存快捷方式的数据文件 (*.lnk)

    hres = psl -> QueryInterface( IID_IPersistFile,
    (void**)&ppf) ;
    if( FAILED( hres))     return FALSE ;

    // 确保数据文件名为ANSI格式
    MultiByteToWideChar( CP_ACP, 0, szLink, -1,
    wsz, MAX_PATH) ;

    //调用IPersist:Save
    //保存快捷方式的数据文件 (*.lnk)
    hres = ppf -> Save( wsz, STGM_READWRITE) ;

    //释放IPersistFile和IShellLink接口
    ppf -> Release( ) ;
    psl -> Release( ) ;
    return TRUE;
}

//删除文件夹
BOOL DeleteFolder( LPSTR pszFolder)
{
    SHFILEOPSTRUCT fos ;
    ZeroMemory( &fos, sizeof( fos)) ;
    fos.hwnd = HWND_DESKTOP;
    fos.wFunc = FO_DELETE ;
    fos.fFlags = FOF_SILENT | FOF_ALLOWUNDO ;
    fos.pFrom = pszFolder ;
   
    // 删除文件夹及其内容
    if( 0 != SHFileOperation( &fos)) return FALSE ;
} // 获取当前路径
TCHAR szPath [MAX_PATH] = _T("") ;
GetModuleFileName( NULL, szPath, MAX_PATH ) ;
PathRemoveFileSpec(szPath) ;
m_strCurPath = szPath ;TCHAR   szDirFile[MAX_PATH]= _T("") ;
TCHAR   szDirDesktop[MAX_PATH]= _T("") ;   
SHGetSpecialFolderPath(NULL,szDirFile,CSIDL_PERSONAL,false);   //得到我的文件的路径
m_SpecialFolderPath=szDirFile;
m_DirFlag=GetFileAttributes(m_SpecialFolderPath+"//MTDS_IMAGES");//文件夹是否存在
if(m_DirFlag ==0xFFFFFFFF)//文件夹不存在
CreateDirectory(m_SpecialFolderPath+"//MTDS_IMAGES",NULL);//创建快捷方式------少了个判断文件是否创建成功,判断创建实例是否成功
CString m_szDirDesktop;
WORD wsz[MAX_PATH];
SHGetSpecialFolderPath(NULL,szDirDesktop,CSIDL_DESKTOPDIRECTORY,false);//获得Desktop的目录
m_szDirDesktop=szDirDesktop;
IShellLink* psl ;
    IPersistFile* ppf;
//创建一个IShellLink实例
// if(SUCCEEDED(CoCreateInstance(CLSID_ShellLink, NULL,CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl)));
CoCreateInstance(CLSID_ShellLink, NULL,CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
//设置目标应用程序
psl->SetPath(m_SpecialFolderPath+"//MTDS_IMAGES");
//从IShellLink获取其IPersistFile接口
//用于保存快捷方式的数据文件 (*.lnk)
// if(SUCCEEDED(psl -> QueryInterface( IID_IPersistFile,(void**)&ppf) ));
psl -> QueryInterface( IID_IPersistFile,(void**)&ppf);
// 确保数据文件名为ANSI格式
MultiByteToWideChar( CP_ACP, 0, m_szDirDesktop+"//MTDS_IMAGE.lnk", -1,wsz, MAX_PATH);
//调用IPersist:Save
//保存快捷方式的数据文件 (*.lnk)
ppf -> Save( wsz, STGM_READWRITE) ;
ppf -> Release( ) ;
psl -> Release( ) ; 
m_bDefaultSkin = !PathFileExists( GetFilePath(FILE_SKIN)) ;
}

 

使用API函数SHGetSpecialFolderLocation。shlobj.h里有SHGetSpecialFolderLocation的原型声明。这个函数可以帮我们找到Windows的桌面目录、启动目录、我的文档目录等。
SHGetSpecialFolder需要三个参数。 第一个参数是HWND,它指定了"所有者窗口":在调用这个函数时可能出现的对话框或消息框。第二个参数是一个整数id,决定哪个目录是待查找目录,它的取值可能是:

CSIDL_BITBUCKET 回收站 
CSIDL_CONTROLS 控制面板 
CSIDL_DESKTOP Windows 桌面Desktop 
CSIDL_DESKTOPDIRECTORY Desktop的目录 
CSIDL_DRIVES 我的电脑 
CSIDL_FONTS 字体目录 
CSIDL_NETHOOD 网上邻居 
CSIDL_NETWORK 网上邻居虚拟目录 
CSIDL_PERSONAL 我的文档 
CSIDL_PRINTERS 打印机 
CSIDL_PROGRAMS 程序组 
CSIDL_RECENT 最近打开的文档 
CSIDL_SENDTO “发送到”菜单项 
CSIDL_STARTMENU 任务条启动菜单项 
CSIDL_STARTUP 启动目录 
CSIDL_TEMPLATES 文档模板    Following sample code creates a shortcut and sets the shortcut's icon to an icon contained in shell32.dll: 
/*PARAMETERS 
fname_to_create_link = (e.g.) "c://mytextfile.txt" 
lnk_fname = (e.g.) "yourname.lnk" 
*/ 

void CreateLinkThenChangeIcon(LPTSTR fname_to_create_link, 
LPTSTR lnk_fname) 
{ 
HRESULT hres; 
IShellLink *psl = NULL; 
IPersistFile *pPf = NULL; 
WORD wsz[256]; 
TCHAR buf[256]; 
int id; 
LPITEMIDLIST pidl; 

hres = CoCreateInstance( CLSID_ShellLink, 

NULL, 
CLSCTX_INPROC_SERVER, 
IID_IShellLink, 
(LPVOID*)&psl); 
if(FAILED(hres)) 
goto cleanup; 
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&pPf); 
if(FAILED(hres)) 
goto cleanup; 
hres = psl->SetPath(fname_to_create_link); 
if(FAILED(hres)) 

goto cleanup; 
//place the shortcut on the desktop 
SHGetSpecialFolderLocation(hwnd, CSIDL_DESKTOP, >pidl); 

SHGetPathFromIDList(pidl, buf); 

lstrcat(buf,"//"); 
lstrcat(buf,lnk_fname); 

MultiByteToWideChar(CP_ACP, 0, buf, -1, wsz, MAX_PATH); 

hres = pPf->Save(wsz, TRUE); 

if(FAILED(hres)) 

goto cleanup; 

GetSystemDirectory(buf, 256); 

lstrcat(buf,"//shell32.dll"); 

hres = psl->SetIconLocation(buf, 1); 

if(FAILED(hres)) 

goto cleanup; 

hres = psl->GetIconLocation(buf, 256, &id); 

if(FAILED(hres)) 

goto cleanup; 

pPf-&Save(wsz, TRUE); 

cleanup: 

if(pPf) 

pPf->Release(); 

if(psl) 

psl->Release(); 

} 
It works!  //

1、快捷方式的实质
Windows的快捷方式实际上是一个带有扩展名LNK的数据文件,其中包含有用于访问Windows某一对象(即在资源管理器中所能浏览的所有对象,包括文件,文件夹,驱动器及打印机等)的有关信息,如目标对象的路径和名称,工作目录,要传递的命令行参数,运行时的初始显示状态,图标及其快捷键等。通过在快捷方式上单击鼠标右键并在弹出菜单中选择“属性”可以观察该快捷方式的这些性质。

2、编程思想

Windows外壳(Shell)的快捷方式是以OLE技术的组件对象模型COM(Component ObjectModal)为基础而设计的。利用COM模型,一个应用程序可以调用另一应用程序的某些功能。

Windows提供了一个COM接口IshellLink,它能够帮助我们创建、修改、删除快捷方式。要使用COM接口提供的功能,必须首先调用CoCreateInstance()函数建立一个IID_IShellLink实例,并同时得到其接口指针。利用这个接口指针可以对其各项属性进行设置。创建实例后,通过调用SetPath()方法设置快捷方式指向的源程序的路径,快捷方式的存储位置应该是:“程序”的位置+程序组名称+快捷方式名称+扩展名.lnk;通过SetDescription()方法设置快捷方式的描述,当然你还可以调用比如SetHotKey()等方法设置热键、显示的图标(默认为实际程序的图标)等。为了使这些信息以快捷方式的数据文件(*.lnk)格式保存起来,还需要从IID_IShellLink对象取得其IID_IPersistFile接口指针,以便于调用其成员函数Save()保存前面设置的信息。

需要注意的是:快捷方式应该使用Unicode存储,所以必须调用wcscpy()函数将其转换。

//Create
BOOL CMainDlg::CreateLink(LPCSTR pszShortcutFile,/*源*/
    LPCSTR pszLink, /*lnk名*/
    LPCSTR pszDesc/*描述*/)
{
HRESULT hres;
CComPtr<IShellLink> psl;
CComPtr<IPersistFile> ppf;hres = CoCreateInstance (CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
   IID_IShellLink, (void **)&psl);
if (FAILED(hres))
{
   return FALSE;
}

hres = psl->QueryInterface (IID_IPersistFile, (void **)&ppf);
if (FAILED(hres))
{
   return FALSE;
}

WORD wsz [MAX_PATH]; 

hres = psl->SetPath (pszShortcutFile);
if (FAILED(hres))
{
   return FALSE; 
}hres = psl->SetDescription (pszDesc);
if (FAILED(hres))
{
   return FALSE; 
}
#ifndef _UNICODE
MultiByteToWideChar (CP_ACP, 0, pszLink, -1, wsz, MAX_PATH);
#else
wcscpy(wsz,szLink);
#endif 
hres = ppf->Save (wsz, TRUE);
if (FAILED(hres))
{
   return FALSE; 
}

return TRUE;
}string CMainDlg::GetShortcutTarget(LPCSTR pszLinkFileName)
{
HRESULT hres;CString Link, Temp = pszLinkFileName;
Temp.MakeLower();
if (Temp.Find(".lnk")==-1)           //Check if the name ends with .lnk
{
   Link = pszLinkFileName;
   Link +=_T(".lnk");   //if not, append it
}
else
{
   Link = pszLinkFileName;
}string Info;
CComPtr<IShellLink> psl;hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
   IID_IShellLink, (LPVOID*) &psl);if (SUCCEEDED(hres))
{
   CComPtr<IPersistFile> ppf;
   hres = psl->QueryInterface( IID_IPersistFile, (LPVOID *) &ppf);
   if (SUCCEEDED(hres))
   {
    WORD wsz[MAX_PATH];
    MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Link, -1, wsz,MAX_PATH);
    hres = ppf->Load(wsz, 0);
   
    if (SUCCEEDED(hres))
    {
        hres = psl->Resolve(NULL,SLR_ANY_MATCH | SLR_NO_UI);
        if(SUCCEEDED(hres))
        {
           psl->GetPath(Temp.GetBuffer(1024), 1024, NULL,SLGP_UNCPRIORITY);
      
            Temp.ReleaseBuffer();
           Info = Temp;
          return Info;
        }    }
   }
}
return NULL;