通过手工操作建立这些菜单项或快捷方式并不复杂,在一般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_FILEstatic char THIS_FILE[] = __FILE__;#endif//起始文件夹的PIDLint nBeginAt=CSIDL_DESKTOPDIRECTORY;......//浏览文件夹BOOL BrowseForFolder(LPITEMIDLIST pidlRoot,//浏览开始处的PIDLLPITEMIDLIST *ppidlDestination,//浏览结束时所选择的PIDLLPCSTR 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()函数将其转换。//CreateBOOL 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 _UNICODEMultiByteToWideChar (CP_ACP, 0, pszLink, -1, wsz, MAX_PATH);#elsewcscpy(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;